functools — The Functional Toolkit
Python’s functools module is the standard library’s collection of higher-order function utilities, tools
that work with, modify, or enhance other functions.
While map(), filter(), and reduce() handle data transformation, functools handles function transformation: fixing
arguments, caching results, generating boilerplate, and enabling dispatch based on type.
The module covers four broad concerns:
- Argument binding — fix some arguments of a function ahead of time (
partial) - Memoization — cache results to avoid recomputation (
lru_cache,cache) - Class utilities — reduce boilerplate in class definitions (
total_ordering) - Type dispatch — route function calls based on argument type (
singledispatch)
partial — Fix Arguments Ahead of Time
Section titled “partial — Fix Arguments Ahead of Time”partial creates a new function with some arguments pre-filled. It is useful when you have a general function but need a specialised version of it for a specific context.
from functools import partial
def power(base, exponent): return base ** exponent
square = partial(power, exponent=2) # fix exponent=2cube = partial(power, exponent=3) # fix exponent=3
print(square(5)) # 25print(cube(3)) # 27
# natural fit with map — no lambda needednumbers = [1, 2, 3, 4, 5]print(list(map(square, numbers))) # [1, 4, 9, 16, 25]A practical example — a logger with a fixed level:
from functools import partial
def log(level, message): print(f"[{level}] {message}")
info = partial(log, "INFO")error = partial(log, "ERROR")
info("Server started") # [INFO] Server startederror("Connection refused") # [ERROR] Connection refusedlru_cache / cache — Memoization
Section titled “lru_cache / cache — Memoization”Both decorators cache the results of a function call so repeated calls with the same arguments return instantly from the cache. lru_cache has a configurable size limit, while cache is equivalent to lru_cache(maxsize=None) — unlimited.
from functools import lru_cache, cache
@lru_cache(maxsize=128)def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2)
@cache # unlimited cachedef factorial(n): if n <= 1: return 1 return n * factorial(n - 1)
print(fibonacci(50)) # instant — results cachedprint(fibonacci.cache_info()) # CacheInfo(hits=48, misses=51, ...)total_ordering — Fill in Comparison Methods
Section titled “total_ordering — Fill in Comparison Methods”Defining all six comparison methods (__eq__, __lt__, __le__, __gt__, __ge__, __ne__) for a class is tedious and repetitive. total_ordering lets you define just __eq__ and one of the ordering methods, and fills in the rest automatically.
from functools import total_ordering
@total_orderingclass Version: def __init__(self, major, minor, patch): self.major = major self.minor = minor self.patch = patch
def __eq__(self, other): return (self.major, self.minor, self.patch) == \ (other.major, other.minor, other.patch)
def __lt__(self, other): # define ONE ordering method return (self.major, self.minor, self.patch) < \ (other.major, other.minor, other.patch)
# __le__, __gt__, __ge__ are auto-generated by @total_ordering
v1 = Version(1, 2, 0)v2 = Version(1, 3, 0)
print(v1 < v2) # Trueprint(v1 > v2) # False ← auto-generatedprint(v1 <= v2) # True ← auto-generatedprint(sorted([Version(2, 0, 0), Version(1, 0, 0), Version(1, 5, 0)]))singledispatch — Function Overloading by Type
Section titled “singledispatch — Function Overloading by Type”singledispatch allows a single function name to have different implementations depending on the type of the first argument — similar to method overloading in statically typed languages, but resolved at runtime.
from functools import singledispatch
@singledispatchdef process(value): raise TypeError(f"Unsupported type: {type(value)}")
@process.register(int)def _(value): return f"Integer: {value * 2}"
@process.register(str)def _(value): return f"String: {value.upper()}"
@process.register(list)def _(value): return f"List of {len(value)} items"
print(process(42)) # Integer: 84print(process("hello")) # String: HELLOprint(process([1, 2, 3])) # List of 3 itemsThe base function decorated with @singledispatch acts as the fallback for any unregistered type. Each @process.register adds a specialised implementation for a specific type.