求类似functools.cached_property的方法缓存装饰器流行实现
实现类似
functools.cached_property的方法缓存装饰器(无需对象可哈希) 如果你需要给类方法实现和cached_property逻辑一致的缓存——将缓存数据存在对象自身的__dict__中,而非全局字典,从而允许对象不可哈希——下面是几种可靠的方案:
一、手动实现轻量版装饰器
这是最灵活的方案,完全符合需求,且无需依赖任何第三方库:
import functools def cached_method(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): # 为当前对象初始化专属缓存字典 if not hasattr(self, '_cached_method_store'): self._cached_method_store = {} # 生成唯一缓存键:结合方法名、位置参数和关键字参数 # 用frozenset处理kwargs,保证无序的kwargs能生成一致的键 cache_key = (func.__qualname__, args, frozenset(kwargs.items())) if cache_key not in self._cached_method_store: self._cached_method_store[cache_key] = func(self, *args, **kwargs) return self._cached_method_store[cache_key] return wrapper
用法示例
class UnhashableObj: def __init__(self, base_val): self.base_val = base_val # 手动禁用对象的哈希能力,模拟不可哈希场景 self.__hash__ = None @cached_method def calculate(self, multiplier): print("执行计算逻辑...") return self.base_val * multiplier # 测试不可哈希对象的缓存效果 obj_a = UnhashableObj(5) print(obj_a.calculate(2)) # 输出:执行计算逻辑... 10 print(obj_a.calculate(2)) # 直接返回缓存值10,无计算输出 obj_b = UnhashableObj(3) print(obj_b.calculate(2)) # 输出:执行计算逻辑... 6
这个装饰器的核心逻辑是:给每个对象单独维护一个缓存字典,缓存键由方法标识和调用参数组合而成,完全避免了对对象哈希性的要求。
二、使用流行第三方库的现成实现
如果你的项目已经依赖一些主流类工具库,直接用它们的内置实现更省心:
1. attrs库的@cached_method
attrs是Python生态中非常流行的类简化库,自带的@cached_method装饰器完全满足你的需求——缓存数据存储在对象自身,无需对象可哈希,且经过了充分的测试优化。
首先安装库:
pip install attrs
用法示例
from attrs import define, cached_method @define class UnhashableObj: base_val: int # 禁用哈希 __hash__ = None @cached_method def calculate(self, multiplier: int): print("执行计算逻辑...") return self.base_val * multiplier
attrs的cached_method内部逻辑和手动实现类似,但额外处理了一些边界情况(比如方法继承、参数类型兼容性等),稳定性更高。
为什么不用functools.cache/lru_cache?
functools模块的这两个缓存装饰器,其缓存字典是绑定在装饰器闭包中的,缓存键需要包含对象实例本身,因此要求对象必须可哈希。而我们的方案将缓存数据存在对象自身的属性中,从根源上避开了这个限制。
内容的提问来源于stack exchange,提问作者Darkdragon84
相关产品推荐
相关产品推荐

