【问题标题】:Python LRU Cache Decorator Per Instance每个实例的 Python LRU 缓存装饰器
【发布时间】:2013-02-03 11:19:14
【问题描述】:

使用此处找到的 LRU 缓存装饰器: http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/

from lru_cache import lru_cache
class Test:
    @lru_cache(maxsize=16)
    def cached_method(self, x):
         return x + 5

我可以用它创建一个装饰类方法,但它最终会创建一个全局缓存,适用于类 Test 的 所有 实例。但是,我的意图是为每个实例创建一个缓存。因此,如果我要实例化 3 个测试,我将拥有 3 个 LRU 缓存,而不是所有 3 个实例的 1 个 LRU 缓存。

我知道发生这种情况的唯一迹象是,当在不同的类实例修饰方法上调用 cache_info() 时,它们都返回相同的缓存统计信息(这极不可能发生,因为它们正在与非常不同的参数进行交互):

CacheInfo(hits=8379, misses=759, maxsize=128, currsize=128)
CacheInfo(hits=8379, misses=759, maxsize=128, currsize=128)
CacheInfo(hits=8379, misses=759, maxsize=128, currsize=128)

是否有一个装饰器或技巧可以让我轻松地让这个装饰器为每个类实例创建一个缓存?

【问题讨论】:

  • 请记住,装饰器只是def method: pass; method = decorate(method) 的语法糖。因此,您可以机械地翻译它以在您的 __init__ 中创建装饰方法。
  • 你确定你知道什么是“类方法”吗?因为我认为你寻找一个正常的方法。如果类方法是特定于实例的,则根据定义,它是实例的普通方法。或者为什么你甚至需要一个类方法?或者你为什么想要一个“每个实例”的缓存?

标签: python caching decorator lru


【解决方案1】:

假设您不想修改代码(例如,因为您希望能够仅移植到 3.3 并使用 stdlib functools.lru_cache,或者使用 PyPI 中的 functools32 而不是复制和粘贴配方到您的代码中),有一个明显的解决方案:为每个实例创建一个新的装饰实例方法。

class Test:
    def cached_method(self, x):
         return x + 5
    def __init__(self):
         self.cached_method = lru_cache(maxsize=16)(self.cached_method)

【讨论】:

  • 太棒了。但是我怎样才能为只获取 @property 做到这一点?
  • @Gilly 你没有,你只需创建一个 _variable 属性并将缓存的值存储在那里。
  • @abarnert 在你的例子中cached_method 是一个变量和一个方法名称,在构造函数变量中,变量self.cached_method 存储cached_method 方法的值?
  • 一个微妙的点是,这会创建一个self -> lru_cache instance -> self 的引用循环。这通常没问题,但值得注意,因为它会将其从通过简单的引用计数进行清理转变为通过最终且可能禁用的垃圾收集进行清理。
  • 注意: 将此答案与魔术方法(如__call__)一起使用,请参阅this。简而言之,使用__call__ 方法,答案需要更改为:self.__class__.__call__ = lru_cache(maxsize=16)(self.__class__.__call__)
【解决方案2】:

这个怎么样:一个 function 装饰器,在每个实例上第一次调用它时用 lru_cache 包装 方法

def instance_method_lru_cache(*cache_args, **cache_kwargs):
    def cache_decorator(func):
        @wraps(func)
        def cache_factory(self, *args, **kwargs):
            print('creating cache')
            instance_cache = lru_cache(*cache_args, **cache_kwargs)(func)
            instance_cache = instance_cache.__get__(self, self.__class__)
            setattr(self, func.__name__, instance_cache)
            return instance_cache(*args, **kwargs)
        return cache_factory
    return cache_decorator

像这样使用它:

class Foo:
    @instance_method_lru_cache()
    def times_2(self, bar):
        return bar * 2

foo1 = Foo()
foo2 = Foo()

print(foo1.times_2(2))
# creating cache
# 4
foo1.times_2(2)
# 4

print(foo2.times_2(2))
# creating cache
# 4
foo2.times_2(2)
# 4

Here's a gist on GitHub 带有一些内联文档。

【讨论】:

    【解决方案3】:

    这些天,methodtools 可以工作

    from methodtools import lru_cache
    class Test:
        @lru_cache(maxsize=16)
        def cached_method(self, x):
             return x + 5
    

    你需要安装methodtools

    pip install methodtools
    

    如果你还在用py2,那么functools32也是必需的

    pip install functools32
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-29
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 2013-02-12
      • 2011-05-25
      • 1970-01-01
      相关资源
      最近更新 更多