Stop Rewriting Code in Python: Start Using functools Now

The functools module is a treasure chest of powerful tools for cleaner, DRYer Python code.

Stop Rewriting Code in Python: Start Using functools Now
Photo by Dan Cristian Pădureț on Unsplash

If you’re writing the same logic twice, you’re not using Python to its full potential.

Stop Rewriting Code in Python: Start Using functools Now

If you’ve ever found yourself duplicating logic, caching results, or wrapping functions in Python — there’s a good chance you could’ve avoided all that by using a powerful but often underused module: functools.

The functools module is part of Python’s standard library and is designed to help with higher-order functions — the kind that either take other functions as arguments or return them. But beyond that, it offers a suite of tools that make your code more reusable, more efficient, and yes — way more Pythonic.

In this article, we’ll explore how functools can save you from rewriting code and help you write cleaner, smarter programs.

Why You Should Care About functools

Before diving into code, let’s answer the “why”:

  • DRY Principle (Don’t Repeat Yourself): Stop repeating function logic again and again.
  • Cleaner Decorators: You’ve probably written decorators without preserving metadata. functools fixes that.
  • Performance Gains: You can cache expensive function calls with almost zero effort.
  • Custom Sorting & Comparison: Make your objects comparable without writing boilerplate.
Let’s get into the fun part.

1. @functools.lru_cache: Cache Like a Pro

Have a function that takes a while to run? Use lru_cache to memoize its results automatically.

import time 
from functools import lru_cache 
 
@lru_cache(maxsize=128) 
def slow_fib(n): 
    time.sleep(1) 
    if n < 2: 
        return n 
    return slow_fib(n-1) + slow_fib(n-2) 
 
print(slow_fib(35))  # Runs fast after first call

Without changing your function logic, you’ve added memoization — a technique that can reduce computation time dramatically for recursive or expensive functions.

2. @functools.wraps: The Decorator's Best Friend

When writing custom decorators, you might notice that the original function’s metadata (like its name and docstring) gets lost.

from functools import wraps 
 
def my_decorator(func): 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
        print("Doing something before the function call.") 
        return func(*args, **kwargs) 
    return wrapper 
 
@my_decorator 
def greet(name): 
    """Say hello to someone.""" 
    print(f"Hello, {name}!") 
 
print(greet.__name__)  # greet 
print(greet.__doc__)   # Say hello to someone.

Without @wraps, you’d see <function wrapper> instead of greet, and you'd lose the original docstring. Bad for debugging and documentation.

3. functools.partial: Pre-Fill Your Functions

Sometimes you call the same function with similar arguments again and again. partial helps you lock in some of those arguments ahead of time.

from functools import partial 
 
def multiply(x, y): 
    return x * y 
 
double = partial(multiply, 2) 
print(double(10))  # 20

Think of partial() as creating a "preset" version of your function.

This simplify API calls, unit conversions, or pre-configured operations.

4. functools.total_ordering: Skip the Boilerplate

Ever needed to define all comparison operators for a class? With total_ordering, you only need to define __eq__ and one other (__lt__, __gt__, etc.), and Python will generate the rest.

from functools import total_ordering 
 
@total_ordering 
class Person: 
    def __init__(self, age): 
        self.age = age 
 
    def __eq__(self, other): 
        return self.age == other.age 
 
    def __lt__(self, other): 
        return self.age < other.age 
 
p1 = Person(25) 
p2 = Person(30) 
 
print(p1 <= p2)  # True 
print(p1 > p2)   # False

No more typing __le__, __ge__, __ne__ manually. One decorator. Done.

5. functools.reduce: Condense Your Logic

reduce() lets you apply a rolling computation to a sequence of items.

from functools import reduce 
 
numbers = [1, 2, 3, 4] 
product = reduce(lambda x, y: x * y, numbers) 
print(product)  # 24

While not always necessary (Python’s sum and math.prod cover many cases), reduce shines when you want custom accumulation logic.

When Not to Use functools

Although powerful, functools isn’t always the answer.

  • Avoid reduce() for very readable logic that could be achieved with a simple loop.
  • Be cautious with lru_cache when your function depends on changing state or external inputs — caching could give stale results.
  • Don’t overuse partial() if it confuses your team — clarity matters more than cleverness.

Final Thoughts: Code Smarter, Not Harder

The Python standard library is full of gems that can supercharge your code — and functools is one of the brightest. It helps you avoid code duplication, improve readability, and optimize performance — all with minimal effort.

So next time you’re tempted to rewrite a function, copy some logic, or build caching from scratch — pause. You might just need functools.


Bonus Tip: Combine lru_cache with recursive functions and see your performance skyrocket in real time. Try it on problems like Fibonacci, pathfinding, or dynamic programming challenges.


If you enjoyed this article, follow me for more practical Python tips that actually make your code better. Less fluff, more function.

Happy coding! 🐍

Photo by Dan Loftus on Unsplash