Batu Lab NotesPractical developer guides

Clear an lru_cache when its source rule changes

By Batu ยท English technical notes

Also published in our Blogger archive.

Clear an lru_cache when its source rule changes

By Batu

functools.lru_cache keys results from function arguments, not from mutable data the function happens to read. In this example, shipping_cost("standard") reads the rates dictionary but accepts only service as its argument. After changing rates["standard"] from 5 to 7, the cached call still returns 5 because its key is unchanged.

The rule-update boundary is therefore the right place to call the wrapper's cache_clear() method. set_rate() updates the dictionary and immediately clears every cached shipping result. A later call recomputes the standard price and returns 7. cache_info() confirms that the final call created one cache miss after the clear; the example prints only stable business results rather than implementation counters.

This pattern is appropriate when all cached outputs depend on one small, mutable rule set. cache_clear() invalidates the entire cache, not only the changed service, so it can be unnecessarily expensive for a large cache or frequent updates. In that case, include an immutable rule version in the cached function's arguments, or use a cache that supports targeted invalidation. The cache also does not synchronize a multi-step external update protocol; protect shared mutable rules with appropriate application-level concurrency control when threads can update them.

AI assistance was used to draft this article.

from functools import lru_cache

rates = {"standard": 5, "express": 12}


@lru_cache(maxsize=8)
def shipping_cost(service: str) -> int:
    return rates[service]


def set_rate(service: str, amount: int) -> None:
    rates[service] = amount
    shipping_cost.cache_clear()


before_change = shipping_cost("standard")
rates["standard"] = 7
stale_value = shipping_cost("standard")
set_rate("standard", 7)
after_clear = shipping_cost("standard")

assert before_change == 5
assert stale_value == 5
assert after_clear == 7
assert shipping_cost.cache_info().misses == 1
print(before_change)
print(stale_value)
print(after_clear)

Expected stdout:

5
5
7

Sources