Lru cache python

PYTHON Published Updated 4 mins read By Leon Wei
Lru cache python cover image

Quick summary

Summarize this blog with AI

functools.lru_cache remembers function results in the current Python process. Use it when the same hashable inputs should produce the same reusable result and recomputation is expensive. This lesson shows eviction, cache statistics, invalidation, and the limits that matter in a real program.

The examples use only the standard library and were tested with Python 3.12.14. Run the blocks in order in a fresh interpreter.

Observe a bounded LRU cache

This deliberately small cache stores two argument/result pairs. The list records actual function executions so that a cache hit is visible without a timing benchmark.

from functools import lru_cache

executed = []

@lru_cache(maxsize=2)
def square(number):
    executed.append(number)  # Instrumentation for this demonstration only.
    return number * number

print([square(n) for n in (2, 3, 2, 4, 3)])
print(executed)
print(square.cache_info())

Expected output:

[4, 9, 4, 16, 9]
[2, 3, 4, 3]
CacheInfo(hits=1, misses=4, maxsize=2, currsize=2)

The second call with 2 hits the cache and makes that entry most recently used. Adding 4 then evicts 3, so the final call with 3 recomputes its result. A hit avoids the function body entirely; do not put required logging, writes, or other business side effects inside a cached function.

Measure reuse before increasing the size

cache_info() reports hits, misses, the entry limit, and the current entry count. Compare the avoided work with the memory occupied by real arguments and results. A size of 128 limits entries, not bytes. maxsize=None removes eviction, so a stream of new keys can keep growing the cache.

print(square.cache_parameters())
print(square.__wrapped__(5))
print(square.cache_info())

Expected output:

{'maxsize': 2, 'typed': False}
25
CacheInfo(hits=1, misses=4, maxsize=2, currsize=2)

__wrapped__ calls the original function without reading or updating the cache. It is useful for a correctness comparison, not a persistence API.

Choose a stable, hashable input contract

Lists cannot be cache keys. If a function accepts an immutable sequence of numbers, make that contract explicit with a tuple. A tuple containing a list is still unhashable. Converting a changing object into a tuple is only correct if the tuple fully captures every input that affects the result.

@lru_cache(maxsize=32)
def total(values):
    return sum(values)

print(total((2, 3, 5)))
try:
    total([2, 3, 5])
except TypeError as exc:
    print(type(exc).__name__)

Expected output:

10
TypeError

Prefer a consistent calling convention. Positional versus keyword calls, and different keyword orders, can occupy separate entries. Use typed=True when immediate argument types must be separated; it does not recursively distinguish all types inside containers.

Invalidate results when the source changes

A cache does not notice changes to a file, database, configuration, or global variable. This small dictionary makes that stale-result problem reproducible. Clear the cache after a known change, or include a meaningful source revision in the function's arguments.

prices = {'book': 12}

@lru_cache(maxsize=16)
def price_for(sku):
    return prices[sku]

print(price_for('book'))
prices['book'] = 15
print(price_for('book'))
price_for.cache_clear()
print(price_for('book'))
print(price_for.cache_info())

Expected output:

12
12
15
CacheInfo(hits=0, misses=1, maxsize=16, currsize=1)

Return immutable results when practical. A cached list or dictionary is the same object on a later hit; caller mutation can change what subsequent callers see. Exceptions raised by the function are not successful return values and are not stored as result entries.

Persistence is a separate design

lru_cache has no supported cache_update() operation and no public export/import interface for its entries. Its state disappears when the process ends. If a restart must preserve results, design a separate persistent store with a schema, source-version key, expiry policy, and validation. Do not load untrusted pickle data: deserialization can execute code. JSON is often suitable for simple records, but it still needs size and schema checks.

Concurrency and asynchronous functions

The cache's internal bookkeeping is thread-safe, but simultaneous misses may execute the wrapped function more than once. That is not an exactly-once guarantee. Do not decorate an async def with this synchronous cache: it would cache a coroutine object rather than a completed reusable result. Choose an explicitly asynchronous design if that is the requirement.

Decide whether caching belongs here

First check that results can be reused, inputs are complete and hashable, and staleness is acceptable or controlled. Then measure a representative workload with and without caching. Keep the simpler uncached function when cache overhead, low reuse, or invalidation complexity outweighs the saved work.

For the key mechanics, see the Python hash-table guide. The authoritative API and serialization limits are documented in functools.lru_cache and the pickle security warning.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles
Python keyerror cover image
python Oct 17, 2026

Python keyerror

Navigate the intricacies of Python's KeyError and exceptions. Learn how unexpected errors occur and the best practices for handling them effecti…

Lru cache python cover image
python Oct 17, 2026

Lru cache python

Dive into caching and LRU (Least Recently Used) cache mechanisms. Understand how caching improves data retrieval and explore LRU's benefits in r…

Python hash table cover image
python Oct 17, 2026

Python hash table

Unpack the mechanics and applications of Python's hash tables, including the hash function, collision handling, performance, and security, for e…

Top python game engines cover image
python Apr 29, 2024

Top python game engines

Explore game creation with Python! Discover how its simplicity & powerful libraries like Pygame and Panda3D make Python a viable choice for indi…