functools: Caching, Partial Functions and Reduce
About 15 minutes
Why this matters
The standard library module functools is full of small tools that make functions more powerful. Three of them solve everyday problems: remembering answers, pre-filling arguments, and combining a list into one value.
The idea
functools.cache is a decorator that remembers the result for each set of arguments. A slow recursive function becomes fast, because each answer is calculated only once:
import functools
@functools.cache
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
print(fib(80)) # instant; without @cache it would take years
print(fib.cache_info()) # hits, misses, size
This only suits functions whose result depends only on their arguments (no randomness, no printing, no changing outside data), and whose arguments are hashable.
functools.partial makes a new function with some arguments already filled in:
def power(base, exp):
return base ** exp
square = functools.partial(power, exp=2)
print(square(7)) # 49
functools.reduce squeezes a list down to one value by applying a two-argument function again and again. The operator module has the arithmetic operators as functions:
import operator
functools.reduce(operator.add, [1, 2, 3, 4], 0) # ((((0+1)+2)+3)+4) = 10
For plain sums, sum() is clearer. reduce shines when there is no built-in, such as a product.
Try it
Run the examples. Call fib.cache_info() after fib(10) and after fib(20) and compare the hits.
Your turn
The starter already has power(base, exp). Add:
fib(n): the recursive Fibonacci (fib(0)is 0,fib(1)is 1) made fast withfunctools.cache.squareandcube: made withfunctools.partialfrompower.product(numbers): multiply all the numbers together withfunctools.reduce; the product of an empty list is1.
Recap
@functools.cacheremembers results: great for pure functions with repeated calls.functools.partial(f, ...)pre-fills arguments and gives a new function.functools.reduce(f, items, start)folds items into one value.operator.add,operator.muland friends are the operators as functions.
Common mistakes
- Caching functions with side effects: the side effect only happens on a cache miss.
- Forgetting the start value in
reduce, which makes an empty list raiseTypeError. - Passing a list to a cached function: lists are not hashable. Use a tuple.