Python functools lru_cache with class methods: release object
This is not the cleanest solution, but it's entirely transparent to the programmer:
import functoolsimport weakrefdef memoized_method(*lru_args, **lru_kwargs): def decorator(func): @functools.wraps(func) def wrapped_func(self, *args, **kwargs): # We're storing the wrapped method inside the instance. If we had # a strong reference to self the instance would never die. self_weak = weakref.ref(self) @functools.wraps(func) @functools.lru_cache(*lru_args, **lru_kwargs) def cached_method(*args, **kwargs): return func(self_weak(), *args, **kwargs) setattr(self, func.__name__, cached_method) return cached_method(*args, **kwargs) return wrapped_func return decorator
It takes the exact same parameters as lru_cache
, and works exactly the same. However it never passes self
to lru_cache
and instead uses a per-instance lru_cache
.
I will introduce methodtools
for this use case.
pip install methodtools
to install https://pypi.org/project/methodtools/
Then your code will work just by replacing functools to methodtools.
from methodtools import lru_cacheclass Foo: @lru_cache(maxsize=16) def cached_method(self, x): return x + 5
Of course the gc test also returns 0 too.
python 3.8 introduced the cached_property
decorator in the functools
module.when tested its seems to not retain the instances.
If you don't want to update to python 3.8 you can use the source code.All you need is to import RLock
and create the _NOT_FOUND
object. meaning:
from threading import RLock_NOT_FOUND = object()class cached_property: # https://github.com/python/cpython/blob/v3.8.0/Lib/functools.py#L930 ...