How to Use functools.cache for Unbounded Memoization in Python

Speed up repeated recursive calls by memoizing function results with Python's built-in functools.cache decorator.

Easy Python 3.9+ Aug 9, 2026 Concurrency & performance 14 views 0 copies

Python code

20 lines
Python 3.9+
```python
import functools
import time


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


if __name__ == "__main__":
    start = time.perf_counter()
    result = fib(30)
    elapsed = time.perf_counter() - start

    print(f"fib(30) = {result}")
    print(f"computed in {elapsed:.6f} seconds")
    print(f"cache info: {fib.cache_info()}")

Output

stdout
fib(30) = 832040
computed in 0.000021 seconds
cache info: CacheInfo(hits=58, misses=31, maxsize=None, currsize=31)

How it works

functools.cache is a thin wrapper around functools.lru_cache(maxsize=None) that caches every result indefinitely — there is no eviction and the cache never shrinks. When fib is first called with n=30, the decorator intercepts each unique argument, stores the return value, and reuses it for any repeated n in the recursion tree. This collapses the exponential call count (over 2.6 million calls without caching) down to just 31 unique computations, which is why the result appears almost instantly. The cache_info() method exposes useful stats: how many times cached values were used (hits) versus how many times the function actually ran (misses). Because the cache lives on the function object, it persists for the life of the process unless you call fib.cache_clear().

Common mistakes

  • Forgetting that functools.cache was added in Python 3.9 — earlier versions need lru_cache(maxsize=None).
  • Using it on functions with mutable or unhashable arguments, like lists or dicts, which cause a TypeError.
  • Assuming memory is unbounded is fine — long-running apps with millions of unique inputs can exhaust RAM and should set a maxsize instead.

Variations

  1. Use `@functools.lru_cache(maxsize=128)` to bound the cache and evict the least-recently-used entries.
  2. Reach for `@functools.cached_property` to memoize a computed attribute on a class instance instead of a plain function.

Real-world use cases

  • Speeding up recursive algorithms like Fibonacci, factorial, or edit-distance calculations in interview-style coding.
  • Memoizing expensive pure function results in data pipelines, such as fetching or computing the same value across many records.
  • Caching parsed configuration or URL metadata across request handlers in a web service to avoid repeated I/O.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Concurrency & performance

Related tutorials and quizzes for this topic.