必须同时实现__eq__和__hash__;否则因__hash__为None导致TypeError,且哈希值需基于不可变字段并与__eq__逻辑一致。

类对象去重必须同时实现 __eq__ 和 __hash__,缺一不可;否则要么无法加入 set/dict,要么去重失效。
为什么只写 __eq__ 会导致 TypeError: unhashable type
Python 默认给自定义类赋予可变对象的哈希行为:若没定义 __hash__,解释器会把 __hash__ 设为 None。此时哪怕 __eq__ 写得再正确,set([obj1, obj2]) 也会直接报错。
- 根本原因:set 和 dict 的底层依赖哈希值快速分桶,再用
__eq__精确比对 - 如果
__hash__是None,对象就被视为“不可哈希”,禁止放入哈希容器 - 仅重写
__eq__不会自动恢复哈希能力——这是显式契约,不是隐式推导
__hash__ 必须基于不可变字段,且与 __eq__ 逻辑一致
哈希值一旦计算出来,就必须在整个对象生命周期内稳定不变;同时,若两个对象 __eq__ 返回 True,它们的 __hash__ 值也必须相等(反之不强制)。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 推荐做法:只用
__eq__中参与比较的、且初始化后不再修改的字段来算哈希,例如self.id或(self.name, self.version) - 错误示例:
__hash__ = lambda self: hash(self.timestamp)—— 若timestamp可变,后续哈希值变化会导致 set 查找失败 - 安全写法:
def __hash__(self): return hash((self.id, self.name)),前提是这两个属性在__init__后不被修改
去重时 set() 和 dict.fromkeys() 行为一致,但要注意对象是否真被“等价”
只要 __eq__ 和 __hash__ 正确实现,set 就能按语义去重;但容易忽略的是:等价判断是否符合业务预期?
立即学习“Python免费学习笔记(深入)”;
- 常见陷阱:用浮点字段参与
__eq__比较(如abs(a.value - b.value) ),此时 <code>__hash__无法精确对应,必然违反哈希契约 → 应避免 - 调试技巧:打印几个对象的
hash(obj)和obj1 == obj2结果,验证一致性 - 性能提示:哈希计算越快越好,避免在
__hash__中调用耗时方法或访问 I/O
最常出问题的地方不在语法,而在字段选择——选了可变字段、漏了 __hash__、或者 __eq__ 和 __hash__ 所依据的字段不一致。这三者只要一个没对齐,去重就变成随机行为。

















