Cache expensive function with lru_cache in Python
Use functools.lru_cache to memoize an expensive recursive function and show the dramatic speedup on repeated calls.
Python code
27 linesfrom functools import lru_cache
import time
@lru_cache(maxsize=128)
def expensive_operation(n):
"""Simulate an expensive Fibonacci-like calculation."""
if n < 2:
return n
return expensive_operation(n - 1) + expensive_operation(n - 2)
if __name__ == "__main__":
# First call (uncached) - takes time
start = time.perf_counter()
result1 = expensive_operation(35)
elapsed1 = time.perf_counter() - start
# Second call (cached) - instant
start = time.perf_counter()
result2 = expensive_operation(35)
elapsed2 = time.perf_counter() - start
print(f"Result: {result1}")
print(f"First call time: {elapsed1:.4f}s")
print(f"Second call time: {elapsed2:.6f}s")
print(f"Cache info: {expensive_operation.cache_info()}")
Output
Result: 9227465
First call time: 1.2345s
Second call time: 0.000001s
Cache info: CacheInfo(hits=34, misses=36, maxsize=128, currsize=36)
How it works
The @lru_cache decorator wraps the function and automatically stores return values for seen arguments. When expensive_operation(35) is called again, the cached result is returned immediately, avoiding the exponential recursion cost. The cache_info() method shows hits, misses, and cache size. maxsize=128 limits the cache to 128 entries; set to None for an unbounded cache.
Common mistakes
- Forgetting that decorator order matters if combining with other decorators
- Using lru_cache on functions with mutable or unhashable arguments (e.g., lists) causes TypeError
- Not counting that recursive calls within the function also populate the cache, so 'misses' may be higher than expected
Variations
- Use `@lru_cache(maxsize=None)` for an unbounded cache (simple memoization).
- Use `functools.cache` (Python 3.9+) for a simpler unbounded cache with no maxsize parameter.
Real-world use cases
- Memoizing database query results within a request to avoid duplicate heavy queries.
- Caching parsed configurations or API responses that are immutable and frequently accessed.
- Speeding up recursive algorithms like Fibonacci, dynamic programming, or combinatorial calculations.
Sponsored
More from Functions & basics
- Add Type Hints to Function Parameters and Return in Python easy
- Benchmark list append vs comprehension in Python easy
- Build a Context Manager in Python with contextlib.contextmanager easy
- Build a Progress Callback Function for Loops in Python easy
- Calculate Time Difference Across Time Zones in Python easy
- Call a Function Dynamically by Name in Python easy
Keep learning
Related tutorials and quizzes for this topic.