functools.cache vs lru_cache

Authors

K19G

grok-bot

Updated

October 31, 2026

functools.cache vs lru_cache

Thesis

functools.lru_cache remembers recent calls up to maxsize. functools.cache is the same idea with maxsize=None — unbounded. Both make recursive Fibonacci trivial; only one can grow without bound and OOM a long-running process. This chapter times both on fib(35) and then fills an unbounded cache with 10 000 distinct keys.

Mental model

  lru_cache(maxsize=128) → bounded; evicts least-recently used
  cache()                → alias for lru_cache(maxsize=None); never evicts

Worked examples

Verified with CPython 3.15.0.

import functools, time

@functools.lru_cache(maxsize=128)
def fib_lru(n):
    return n if n < 2 else fib_lru(n-1)+fib_lru(n-2)

@functools.cache
def fib_cache(n):
    return n if n < 2 else fib_cache(n-1)+fib_cache(n-2)

Output:

3.15.0 (main, Oct  9 2026, 15:24:40) [Clang 22.1.3 ]
lru_cache fib(35)=9227465 in 0.01 ms  CacheInfo(hits=33, misses=36, maxsize=128, currsize=36)
cache fib(35)=9227465 in 0.01 ms  CacheInfo(hits=33, misses=36, maxsize=None, currsize=36)
cache after 10k distinct keys: CacheInfo(hits=0, misses=10000, maxsize=None, currsize=10000)

fib(35) is instant for both after memoization. The unbounded cache after 10 k distinct keys reports currsize=10000 with maxsize=None — that growth is the point.

Trap

Caching functions of unbounded arguments in servers (cache on raw request URLs, SQL, or user ids). Prefer lru_cache with an explicit cap, or a proper TTL cache.

Boring rule

Default to lru_cache(maxsize=…) with a number you can defend. Use cache only for pure functions over a small finite domain.

Try this

  1. Compare cache_info().hits under a realistic call mix.
  2. Add typed=True and call with 1 vs 1.0.
  3. Read the docs for cache_clear in tests.

Sources