如何在不泄漏内存的情况下在类中使用functools的lru_cache?在下面的最小示例中,尽管超出了作用域并且没有referrer (除了lru_cache之外),foo
实例也不会被释放。
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_caches的推荐方式是什么?
我知道有两种变通方法:Use per instance caches或make the cache ignore object (但这可能会导致错误的结果)
发布于 2015-11-12 21:26:14
这不是最干净的解决方案,但它对程序员来说是完全透明的:
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
。
发布于 2019-05-05 18:00:25
对于这个用例,我将介绍methodtools
。
用于安装https://pypi.org/project/methodtools/的pip install methodtools
然后,您的代码将只需将functools替换为methodtools即可。
from methodtools import lru_cache
class Foo:
@lru_cache(maxsize=16)
def cached_method(self, x):
return x + 5
当然,gc测试也会返回0。
发布于 2019-10-28 22:18:03
Python3.8在functools
模块中引入了cached_property
装饰器。在测试时,它似乎没有保留实例。
如果你不想更新到Python3.8,你可以使用source code。您只需导入RLock
并创建_NOT_FOUND
对象。含义:
from threading import RLock
_NOT_FOUND = object()
class cached_property:
# https://github.com/python/cpython/blob/v3.8.0/Lib/functools.py#L930
...
https://stackoverflow.com/questions/33672412
复制相似问题