functools.lru_cache
functools.lru_cache
Section titled βfunctools.lru_cacheβWhat it is
Section titled βWhat it isβ@functools.lru_cache is a decorator that memoizes a function: it caches return values keyed by the arguments passed in, so calling the function again with the same arguments returns the cached result instantly instead of recomputing it. βLRUβ (least-recently-used) describes the eviction policy once the cache hits its size limit.
Before this feature
Section titled βBefore this featureβManual memoization means writing and maintaining your own cache dict:
_cache = {}
def fib(n): if n in _cache: return _cache[n] if n <= 1: result = n else: result = fib(n - 1) + fib(n - 2) _cache[n] = result return resultAfter this feature
Section titled βAfter this featureβfrom functools import lru_cache
@lru_cache(maxsize=None) # None = unbounded cachedef fib(n): if n <= 1: return n return fib(n - 1) + fib(n - 2)
fib(35) # fast β without caching, this is exponential and very slow
print(fib.cache_info())# CacheInfo(hits=33, misses=36, maxsize=None, currsize=36)
fib.cache_clear() # wipe the cache manually if neededWhy this is better
Section titled βWhy this is betterβ- No manual cache bookkeeping: one decorator line replaces a hand-rolled dict cache and the
if in cacheboilerplate. - Built-in introspection:
.cache_info()reports hits/misses/size, useful for tuningmaxsizeor confirming the cache is actually helping. - Automatic eviction: with a bounded
maxsize, the least-recently-used entries are dropped once the cache is full, so memory doesnβt grow unbounded β you donβt have to implement that policy yourself.
Key notes / edge cases
Section titled βKey notes / edge casesβ- All arguments must be hashable β
lru_cachecanβt cache calls with alistordictargument (TypeError: unhashable type). - The cache is keyed on the exact arguments, including whether they were passed positionally or by keyword β
f(1, 2)andf(a=1, b=2)are cached as different calls even if the function treats them the same. maxsize=Nonemeans unbounded β fine for a small, finite input space (like memoizingfib), risky for a cache keyed on unbounded/unique inputs (e.g. request IDs), where it would just grow forever.- Because the cache is attached to the function object itself, it persists for the life of the process β a long-running service should size
maxsizedeliberately rather than leave it unbounded for a function called with highly varied arguments.
@lru_cache(maxsize=128) # bounded β oldest unused entries evicted past 128def expensive_lookup(key): ...Quick practice
Section titled βQuick practiceβ-
What does
@lru_cachedo the second time a function is called with the same arguments?Answer
Returns the cached result immediately, without re-running the function body. -
Why canβt you
@lru_cachea function that takes alistargument?Answer
The cache key is built from the arguments, which must be hashable β lists (and dicts) aren't hashable. -
What happens once a bounded
lru_cache(e.g.maxsize=128) is full and a new, uncached call comes in?Answer
The least-recently-used cached entry is evicted to make room β hence "LRU."