如何在Python中缓存方法?

问题描述 投票:0回答:1

我有一个具有大量递归/迭代方法的对象。我想缓存方法响应以加快调用速度。我发现的四种方法是:

  1. 使用内置
    lru_cache
    。这会在类级别创建共享缓存。好处是速度快、易于实施,而且其他人也熟悉它。主要问题是我没有找到清除与特定实例关联的缓存的方法,因此如果有很多实例,缓存将变得非常大,这使得此方法在这种情况下无法使用。
  2. 在类级别创建我自己的缓存版本,允许特定实例的缓存项在被销毁时被清除,就像在这个响应中here
  3. 在实例级别存储结果(使用
    lru_cache
    (如解决方案此处所示)或使用描述符和实例字典)。
  4. 通过返回可调用的新实例(该实例保存第一个
    __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
版本似乎是最直接的。

是否存在缓存方法并在删除实例时清除这些缓存结果的约定?

python caching methods
1个回答
0
投票

这是@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)
© www.soinside.com 2019 - 2024. All rights reserved.