Python functools lru_缓存和类方法:release object

Python functools lru_缓存和类方法:release object,python,caching,python-decorators,lru,functools,Python,Caching,Python Decorators,Lru,Functools,如何在类内使用functools的lru\U缓存而不泄漏内存? 在下面的最小示例中,foo实例将不会被释放,尽管它超出了范围并且没有引用(除了lru_缓存) 但是foo和foo.big(aBigClass)仍然存在 import gc; gc.collect() # collect garbage len([obj for obj in gc.get_objects() if isinstance(obj, Foo)]) # is 1 这意味着Foo/BigClass实例仍然驻留在内存中。即

如何在类内使用functools的lru\U缓存而不泄漏内存? 在下面的最小示例中,
foo
实例将不会被释放,尽管它超出了范围并且没有引用(除了lru_缓存)

但是
foo
foo.big
(a
BigClass
)仍然存在

import gc; gc.collect()  # collect garbage
len([obj for obj in gc.get_objects() if isinstance(obj, Foo)]) # is 1
这意味着Foo/BigClass实例仍然驻留在内存中。即使删除
Foo
(del
Foo
)也不会释放它们

为什么lru_缓存会保留实例?缓存不是使用了一些散列而不是实际的对象吗

建议在类内使用lru_缓存的方法是什么

我知道有两种解决办法:
或者(这可能会导致错误的结果)

这不是最干净的解决方案,但对程序员来说是完全透明的:

import functools
import weakref

def 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

它采用与
lru\u cache
完全相同的参数,并且工作原理完全相同。但是,它从不将
self
传递到
lru\u缓存
,而是使用每个实例
lru\u缓存

methodtools
,我将介绍此用例

pip安装方法工具
安装

然后,您的代码只需将functools替换为methodtools即可工作

从methodtools导入lru\U缓存
Foo类:
@lru_缓存(最大大小=16)
def缓存_方法(self,x):
返回x+5
当然,gc测试也返回0。

python3.8在
functools
模块中引入了decorator。 测试时,它似乎没有保留实例

如果不想更新到python 3.8,可以使用。 您只需导入
RLock
并创建
\u NOT\u FOUND
对象。意思是:

from threading import RLock

_NOT_FOUND = object()

class cached_property:
    # https://github.com/python/cpython/blob/v3.8.0/Lib/functools.py#L930
    ...

这有点奇怪,实例上的函数只在第一次调用时被缓存包装器替换。此外,缓存包装器函数没有添加
lru\u cache
cache\u clear
/
cache\u info
函数(实现这一点是我第一次碰到的)。这似乎不适用于
\uu getitem\uuuu
。你知道为什么吗?如果调用
instance.\uuuu getitem\uuuuu(key)
但不调用
instance[key]
,则它确实有效。这对于任何特殊方法都不起作用,因为这些方法是在类槽中查找的,而不是在实例字典中查找的。设置
obj.\uuuu getitem\uuuu=lambda item:item
不会导致
obj[key]
工作的原因相同。您可以使用任意一种
methodtools.lru\u cache
的行为与
functools.lru\u cache
完全相同,它通过重用
functools.lru\u cache
内部而
ring.lru\u cache
通过在python中重新实现lru存储来建议更多功能。
methodtools.lru\u cache
在一个方法上为类的每个实例使用单独的存储,而
ring.lru
的存储由类的所有实例共享。
from threading import RLock

_NOT_FOUND = object()

class cached_property:
    # https://github.com/python/cpython/blob/v3.8.0/Lib/functools.py#L930
    ...