A Practical Guide to functools.lru_cache in Python

· 3 min read · Updated March 13, 2026 · beginner
stdlib functools caching performance python

If you’ve ever written a function that gets called repeatedly with the same arguments, you know the pain of recomputing the same result over and over. The functools.lru_cache decorator is the antidote to this inefficiency.

What is lru_cache?

lru_cache stands for Least Recently Used cache. It’s a decorator that stores the results of a function in memory, so subsequent calls with the same arguments return the cached result instead of re-executing the function. This technique is called memoization.

Here’s the simplest possible example:

from functools import lru_cache

@lru_cache
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# First call - computes and caches
print(fibonacci(30))  # Takes a moment

# Subsequent calls - instant
print(fibonacci(30))  # Returns cached result

Without caching, computing fibonacci(30) would make over 2 million recursive calls. With lru_cache, it makes exactly 31 calls. The difference is night and day.

The maxsize Parameter

By default, lru_cache stores an unlimited number of results. In production, you almost always want to set a limit:

@lru_cache(maxsize=128)
def expensive_operation(x):
    # Some costly computation
    return x ** 2

When the cache is full and a new entry is added, the least recently used entry gets evicted. Setting maxsize=None disables the size limit (use with caution), while maxsize=0 effectively disables caching entirely.

A power of two (128, 256, 512) is a common choice, but you should size it based on your function’s input space. If you’re caching results for a function that accepts any integer, a small cache might cause thrashing. If the domain is small (like enum values), a small maxsize is fine.

Inspecting the Cache

Sometimes you need to debug or monitor your cache. lru_cache provides two methods for this:

@lru_cache(maxsize=10)
def square(x):
    return x * x

square(5)
square(5)
square(10)
square(3)

print(square.cache_info())
# CacheInfo(hits=1, misses=3, maxsize=10, currsize=3)

The output shows:

  • hits: How many times a cached result was returned
  • misses: How many times the function actually ran
  • maxsize: The maximum cache size
  • currsize: Current number of cached entries

This is invaluable for tuning your cache and spotting when things aren’t working as expected.

Clearing the Cache

There are two ways to clear a cached function’s results:

# Clear just this function's cache
square.cache_clear()

# Or invalidate specific arguments (Python 3.9+)
square.cache_partial_clear(5)  # Only removes the entry for argument 5

cache_clear() is useful when you know the underlying data has changed and you need a fresh start.

When NOT to Use lru_cache

This isn’t a silver bullet. Avoid lru_cache when:

  • Your function has side effects (it modifies state, writes to files, makes network calls)
  • Your arguments are mutable (lists, dicts, custom objects)
  • Your function returns different results for the same inputs (not deterministic)

Also, remember that cached arguments must be hashable. If you’re passing unhashable types, you’ll get a TypeError.

A Real-World Example

Here’s something more practical than fibonacci - fetching data from an API with caching:

from functools import lru_cache
import requests

@lru_cache(maxsize=100)
def get_user(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()

# First call hits the API
user = get_user(42)

# This one returns instantly from cache
user_cached = get_user(42)

Of course, you’d want to handle errors and set cache expiration for real APIs, but this shows the pattern nicely.

Comparison with Other Caching Methods

While lru_cache is the go-to solution for simple in-memory caching, Python offers other options depending on your needs:

  • cachetools: Provides additional cache implementations like TTLCache (time-based expiration) and LFU (least frequently used)
  • diskcache: Caches to disk instead of memory, useful for large datasets
  • functools.cache: Equivalent to @lru_cache(maxsize=None), simpler if you don’t need size limits

For most web applications, consider combining lru_cache with HTTP caching headers or a Redis-backed solution for distributed systems.

See Also