Stop Rewriting Code in Python: Start Using functools Now
The functools module is a treasure chest of powerful tools for cleaner, DRYer Python code.

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! 🐍
