我有一个具有大量递归/迭代方法的对象。我想缓存方法响应以加快调用速度。我发现的四种方法是:
lru_cache
。这会在类级别创建共享缓存。好处是速度快、易于实施,而且其他人也熟悉它。主要问题是我没有找到清除与特定实例关联的缓存的方法,因此如果有很多实例,缓存将变得非常大,这使得此方法在这种情况下无法使用。lru_cache
(如解决方案此处所示)或使用描述符和实例字典)。__get__
上的方法和缓存)并将其分配给具有方法名称的实例,在方法级别存储结果。示例:
import types
class Wrapper:
def __init__(self, func):
self._values = {}
self.func = func
def __call__(self, *args):
key = tuple(args)
if key not in self._values:
self._values[key] = self.func(*args)
return self._values[key]
class Memoizer:
def __init__(self, func):
self.func = func
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, cls):
bound_func = Wrapper(types.MethodType(self.func, instance))
setattr(instance, self.name, bound_func)
return bound_func
class MyClass:
@Memoizer
def my_func(self, x, y):
return x + y
我不想重新发明轮子。 #3 的
lru_cache
版本似乎是最直接的。
是否存在缓存方法并在删除实例时清除这些缓存结果的约定?
这是@cached_method的实现
from functools import wraps
def cached_method(func):
"""
Decorator. Per instance method cache.
Garbage collected with the instance
"""
cache_name = f'_{func.__name__}_cache'
@wraps(func)
def wrapper(self, *args, **kwargs):
key = (args, tuple(kwargs.items()))
if (cache := getattr(self, cache_name, None)) is None:
setattr(self, cache_name, cache := {})
if key in cache:
return cache[key]
result = cache[key] = func(self, *args, **kwargs)
return result
def is_cached(self, *args, **kwargs) -> bool:
key = (args, tuple(kwargs.items()))
if (cache := getattr(self, cache_name, None)) is None:
return False
return key in cache
def get_cached_dict(self) -> dict:
return getattr(self, cache_name, {})
wrapper.is_cached = is_cached
wrapper.get_cached_dict = get_cached_dict
return wrapper
使用方法
class MyClass:
@cached_method
def my_func(self, x, y):
return x + y
a = MyClass()
a.my_func(1,2)
# Also you can get cached dict
a.my_func.get_cached_dict()
# or check if values are cached
a.my_func.is_cached(1,2)