functools Module Overview¶
The functools module provides higher-order functions and operations on callable objects. It's essential for functional programming patterns in Python.
import functools
# or
from functools import partial, wraps, lru_cache, cache, reduce
Module Contents at a Glance¶
| Function | Purpose | Python Version |
|---|---|---|
partial |
Freeze function arguments | 2.5+ |
reduce |
Cumulative fold operation | 2.6+ |
wraps |
Preserve decorator metadata | 2.5+ |
lru_cache |
Bounded memoization (LRU) | 3.2+ |
cache |
Unbounded memoization | 3.9+ |
total_ordering |
Complete comparison ops | 2.7+ |
singledispatch |
Type-based overloading | 3.4+ |
cached_property |
Lazy cached attribute | 3.8+ |
cmp_to_key |
Legacy comparison adapter | 2.7+ |
How functools Fits into Python¶
Relationship to Other Modules¶
functools — higher-order functions, caching, dispatch
├── partial — similar to lambda but preserves metadata
├── reduce — moved here from built-ins in Python 3
├── wraps — essential companion to decorators (Ch 5.6)
├── lru_cache — memoization with eviction policy
├── cache — simpler memoization (Python 3.9+)
└── singledispatch — type-based dispatch
operator — function versions of operators (Ch 5.4)
itertools — iterator combinators (Ch 7.2)
Core Theme: Functions That Transform Functions¶
Every tool in functools takes a callable and produces a new callable:
from functools import partial, lru_cache, wraps
# partial: fix arguments → new function
square = partial(pow, exp=2)
# lru_cache: add memoization → cached function
@lru_cache(maxsize=128)
def fib(n): ...
# wraps: copy metadata → wrapper with correct identity
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
Quick Examples¶
partial — Freeze Arguments¶
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25
print(cube(5)) # 125
reduce — Fold a Sequence¶
from functools import reduce
import operator
numbers = [1, 2, 3, 4, 5]
product = reduce(operator.mul, numbers)
print(product) # 120
lru_cache — Memoize Results¶
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100)) # Instant
print(fibonacci.cache_info())
# CacheInfo(hits=98, misses=101, maxsize=128, currsize=101)
wraps — Preserve Metadata in Decorators¶
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
import time
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__}: {time.perf_counter() - start:.4f}s")
return result
return wrapper
@timer
def process(data):
"""Process data."""
return sum(data)
print(process.__name__) # 'process' (not 'wrapper')
total_ordering — Fill in Comparisons¶
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, major, minor):
self.major = major
self.minor = minor
def __eq__(self, other):
return (self.major, self.minor) == (other.major, other.minor)
def __lt__(self, other):
return (self.major, self.minor) < (other.major, other.minor)
# All six comparisons now work
v1, v2 = Version(1, 0), Version(2, 0)
print(v1 < v2) # True (defined)
print(v1 <= v2) # True (auto-generated)
print(v1 > v2) # False (auto-generated)
singledispatch — Type-Based Overloading¶
from functools import singledispatch
@singledispatch
def process(data):
raise NotImplementedError(f"Cannot process {type(data)}")
@process.register(list)
def _(data):
return [x * 2 for x in data]
@process.register(str)
def _(data):
return data.upper()
print(process([1, 2, 3])) # [2, 4, 6]
print(process("hello")) # HELLO
Choosing the Right Tool¶
| Need | Tool | Example |
|---|---|---|
| Fix some arguments | partial |
square = partial(pow, exp=2) |
| Cache function results | lru_cache / cache |
Recursive DP, expensive queries |
| Write a decorator | wraps |
Logging, timing, retry decorators |
| Fold sequence to value | reduce |
Product, flatten, compose |
| Auto-generate comparisons | total_ordering |
Custom sortable classes |
| Dispatch by type | singledispatch |
Format different data types |
| Cache a property | cached_property |
Expensive computed attributes |
Section Map¶
Each function has a dedicated page with detailed coverage:
- functools.cache — unbounded memoization for recursive algorithms
- functools.lru_cache — bounded memoization with LRU eviction
- functools.partial — partial function application
- functools.reduce — cumulative fold operations
- functools.wraps — metadata preservation for decorators
- functools.total_ordering — auto-generate comparison methods
- functools.singledispatch — single-argument type dispatch
Summary¶
| Concept | Key Point |
|---|---|
| Module purpose | Higher-order functions and callable transformations |
| Most used | wraps, lru_cache, partial |
| Caching | cache (unbounded) vs lru_cache (bounded) |
| Decorators | Always use @wraps(func) |
| Folding | reduce works but prefer built-ins when available |
| Dispatch | singledispatch avoids if/elif type chains |
Key Takeaways:
functoolsis the Swiss army knife for functional programming in Pythonwrapsandlru_cacheare used daily in production codepartialcreates cleaner specialized functions than lambdasreduceis powerful but often less readable than built-in alternativestotal_orderingandsingledispatchreduce boilerplate in OOP code