6

I know about cache_clear. However I wonder how it is possible to clear the cache of a single call. Seeing that cache_clear does not accept an argument I'd think the only way would be to mess with the underlying dictionary directly. What side-effect (if any) that would have?

The goal is to achieve something along the lines of

from functools import lru_cache

@lru_cache()
def f(x):
    print('called with ', x)


f(1)
f.cache_clear(1)  # obviously does not work
f(1)

With the output

called with  1
called with  1
DeepSpace
  • 78,697
  • 11
  • 109
  • 154
  • 1
    This issue/request was raised some years back: https://bugs.python.org/issue28178. But it is rejected and status is closed. – Austin Jun 02 '19 at 08:34
  • @Austin That's unfortunate. Implementing your own cache (however simple it may be) to get a functionality which is pretty much equivalent to `dict.pop(key)` (or `del dict[key]`) seems an overkill – DeepSpace Jun 02 '19 at 08:43
  • [This](https://stackoverflow.com/questions/1988804/what-is-memoization-and-how-can-i-use-it-in-python) thread may be of use. – N Chauhan Jun 02 '19 at 09:17

2 Answers2

3

This should be considered impossible. There is no public API for this, no attribute at all to access the cache, and no uniform format for cache keys. Even if you take drastic measures (gc/ctypes shenanigans) to get at the cache anyway, you're likely to just break the whole LRU system, leak a bunch of memory everywhere, and maybe segfault. The memory management in the implementation isn't designed to be safe in the face of someone messing with the cache directly.

user2357112
  • 260,549
  • 28
  • 431
  • 505
0

I successfully implemented a granular cache_clear() for a Python redis backed lru cache decorator - see gist. This was a totally different implementation of lru_cache than the one that is shipped with Python 3, I could understand it's internals and make the necessary changes.

Specifically, cache_clear() was enhanced so that if you pass parameters the cache is cleared partially - only for that particular call signature. This does indeed allow for more granular cache clearing.

I learnt something deep along the way, which sheds some light on this discussion and may be a reason why Python 3 did not adopt granular cache clearing.

I discovered that such an enhanced cache_clear() behaviour needed to be used with great care since whilst e.g. f(1) and f(param=1) mean the same, the lru caching system will cache those two calls as separate entries. Then when you invalidate one style of call with f.cache_clear(1) this leaves the other style of call f(param=1) still cached and returning stale values - even though semantically these two styles of call are the same. So if you do use cache_clear granularly, make sure you call it repeatedly for all possible parameter signatures that you might have used e.g. f.cache_clear(1); f.cache_clear(param=1).

I suppose there might be an algorithm that can figure out all the possible permutations of parameters (*args, **kwargs) that are equivalent? Then such a granular cache clearing approach would be much safer to use.

abulka
  • 1,316
  • 13
  • 18