Skip to content

functools.lru_cache

@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.

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 result
from functools import lru_cache
@lru_cache(maxsize=None) # None = unbounded cache
def 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 needed
  • No manual cache bookkeeping: one decorator line replaces a hand-rolled dict cache and the if in cache boilerplate.
  • Built-in introspection: .cache_info() reports hits/misses/size, useful for tuning maxsize or 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.
  • All arguments must be hashable β€” lru_cache can’t cache calls with a list or dict argument (TypeError: unhashable type).
  • The cache is keyed on the exact arguments, including whether they were passed positionally or by keyword β€” f(1, 2) and f(a=1, b=2) are cached as different calls even if the function treats them the same.
  • maxsize=None means unbounded β€” fine for a small, finite input space (like memoizing fib), 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 maxsize deliberately rather than leave it unbounded for a function called with highly varied arguments.
@lru_cache(maxsize=128) # bounded β€” oldest unused entries evicted past 128
def expensive_lookup(key):
...
  1. What does @lru_cache do the second time a function is called with the same arguments?

    AnswerReturns the cached result immediately, without re-running the function body.
  2. Why can’t you @lru_cache a function that takes a list argument?

    AnswerThe cache key is built from the arguments, which must be hashable β€” lists (and dicts) aren't hashable.
  3. What happens once a bounded lru_cache (e.g. maxsize=128) is full and a new, uncached call comes in?

    AnswerThe least-recently-used cached entry is evicted to make room β€” hence "LRU."