You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)判断两个对象是否是同一个键的逻辑是:
    1. 两个对象的哈希值必须相等(hash(a) == hash(b))
    2. 两个对象必须满足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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 15:33:12