functools.lru_cache为何区分哈希相同的不同对象?如何让其将初始化参数相同的MyClass实例视为同一缓存键?
让
functools.lru_cache识别初始化参数相同的自定义类实例 你碰到的这个问题,核心在于**lru_cache依赖Python字典的键判断逻辑**:它不仅会用哈希值快速定位候选键,还会通过==运算符验证对象是否真正相等。你的MyClass只重写了__hash__,但默认的__eq__是比较对象的内存地址,所以即使m1a和m1b哈希值相同,它们还是会被判定为不同的键,导致缓存不命中。
解决方法:同时重写__eq__方法
要让lru_cache把初始化参数相同的MyClass实例视为同一个键,你需要给MyClass添加一个基于初始化参数的__eq__方法,和你已经实现的__hash__逻辑保持一致。
修改后的代码如下:
from functools import lru_cache COUNT=0 @lru_cache(maxsize=None) def fnc(*args, **kvargs): global COUNT COUNT=COUNT+1 return COUNT, hash(args[0]), args ,kvargs class MyClass: def __init__(self, *args, **kvargs): self._init_args=(args, frozenset(kvargs.items())) def __hash__(self): return hash(self._init_args) # 添加__eq__方法,基于初始化参数判断相等 def __eq__(self, other): if not isinstance(other, MyClass): return False return self._init_args == other._init_args m1a = MyClass(1) m2a = MyClass(2) m1b = MyClass(1) m2b = MyClass(2) # 现在测试缓存命中情况 print(fnc(m1a)) # (1, ..., (<__main__.MyClass object at 0x...>,), {}) print(fnc(m1a)) # 命中缓存,输出(1, ...) print(fnc(m2a)) # (2, ...) print(fnc(m2a)) # 命中缓存 print(fnc(m1b)) # 命中缓存,输出(1, ...) !!! print(fnc(m2b)) # 命中缓存,输出(2, ...) !!!
关键原理说明
- Python中,字典(以及依赖字典的
lru_cache)判断两个对象是否是同一个键的逻辑是:- 两个对象的哈希值必须相等(
hash(a) == hash(b)) - 两个对象必须满足
a == b(也就是__eq__返回True)
- 两个对象的哈希值必须相等(
- 你之前只满足了第一个条件,第二个条件因为用了默认的
__eq__(比较内存地址),所以m1a != m1b,导致缓存不命中。 - 注意:重写
__eq__时,一定要保证相等的对象哈希值相同(这一点你已经通过_init_args实现了,因为如果self._init_args == other._init_args,它们的哈希值肯定相等),这是Python的规则,否则会导致字典等哈希表结构出现异常。
额外注意事项
- 如果你在
__init__中使用了可变类型的参数(比如普通列表、字典),一定要把它们转换成不可变类型(比如tuple、frozenset)再存入_init_args,否则__hash__会报错,因为可变类型不能被哈希。你用frozenset(kvargs.items())处理关键字参数的方式是正确的。
内容的提问来源于stack exchange,提问作者santos82h
相关产品推荐
相关产品推荐

