如何为依赖类属性的Python类方法配置lru_cache?
问题分析
你的问题出在lru_cache对类方法的缓存逻辑上:@classmethod修饰的test方法被缓存时,缓存的键是(cls, x),但类对象cls的哈希值不会随类属性a的变化而改变。所以即使你修改了Test.a,lru_cache依然会返回之前基于旧a值计算的缓存结果,导致输出不符合预期。
方案1:a变更时清空缓存(推荐,适配a极少变更的场景)
既然a极少变更,最简单的方式就是在修改a时直接清空test方法的缓存,后续调用会自动重新计算新a值对应的结果。只需在__new__方法中添加缓存清空逻辑:
from functools import lru_cache class Test(): a = 1 def __new__(cls, a = 1): cls.a = a cls.test.cache_clear() # 变更a时清空缓存 return super().__new__(cls) @classmethod @lru_cache(maxsize=32) def test(cls, x): return x * cls.a print(Test.test(1), Test(2).test(1), Test.test(1)) # 输出:1 2 2
优点:实现简单,完全符合你要求的调用方式,无需修改test方法的参数结构,适配a极少变更的场景。
方案2:将a纳入缓存键(保留所有(a,x)组合的缓存)
如果需要保留不同a值对应的缓存结果(即使a变更后也不丢弃旧缓存),可以自定义装饰器,自动将类属性a作为缓存键的一部分传入:
from functools import lru_cache, wraps def lru_cache_with_attr(attr_name): def decorator(func): @wraps(func) def wrapper(cls, *args, **kwargs): # 将指定类属性作为额外参数传入缓存函数 return cached_func(cls, getattr(cls, attr_name), *args, **kwargs) @lru_cache(maxsize=32) def cached_func(cls, attr_val, *args, **kwargs): return func(cls, *args, **kwargs) return wrapper return decorator class Test(): a = 1 def __new__(cls, a = 1): cls.a = a return super().__new__(cls) @classmethod @lru_cache_with_attr('a') def test(cls, x): return x * cls.a print(Test.test(1), Test(2).test(1), Test.test(1)) # 输出:1 2 2
优点:保留所有(a,x)组合的缓存结果,后续切换回旧a值时可直接复用缓存;缺点:实现相对复杂,缓存占用会随a的不同值增加。
内容的提问来源于stack exchange,提问作者André
相关产品推荐
相关产品推荐

