Python functools lru_cache with class methods: release object Python functools lru_cache with class methods: release object python python

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    ...