如何在类中使用 functools.lru_cache
而不泄漏内存?
在下面的最小示例中,foo
实例不会被释放,尽管超出范围并且没有引用者(除了 lru_cache
)。
from functools import lru_cache
class BigClass:
pass
class Foo:
def __init__(self):
self.big = BigClass()
@lru_cache(maxsize=16)
def cached_method(self, x):
return x + 5
def fun():
foo = Foo()
print(foo.cached_method(10))
print(foo.cached_method(10)) # use cache
return 'something'
fun()
但是 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_cache
会保留实例?缓存不是使用一些哈希而不是实际对象吗?
在类中使用 lru_cache
s 的推荐方式是什么?
我知道两种解决方法: Use per instance caches或 make the cache ignore object (不过,这可能会导致错误的结果)
最佳答案
这不是最干净的解决方案,但对程序员来说是完全透明的:
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_cache
完全相同的参数,并且工作方式完全相同。但是,它从不将 self
传递给 lru_cache
,而是使用每个实例的 lru_cache
。
关于Python functools lru_cache 与实例方法 : release object,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33672412/