要让Set正确识别深层对象是否重复,必须同时重写__eq__和__hash__方法且逻辑一致:__eq__定义值相等逻辑,__hash__基于相同字段计算哈希值;可变对象应设__hash__ = None;嵌套对象类也需正确定义二者。

要让 Set 正确识别深层对象是否“重复”,关键不是手动遍历比较,而是让对象自己说清楚:什么算相等、什么算不同。Python 的 set 不会递归比对属性,它只认 __hash__ 和 __eq__ 这两个方法——缺一不可,且必须逻辑一致。
必须同时重写 __eq__ 和 __hash__
只改 __eq__ 而不改 __hash__,对象放进 set 后行为不可预测;只改 __hash__ 而不改 __eq__,则相同内容可能被当成不同元素。
-
__eq__定义“值相等”的逻辑,比如两个 Person 实例 name 和 age 都一样,就返回True -
__hash__必须基于和__eq__相同的字段计算,确保相等的对象哈希值也相等(反之不强制) - 如果类可变(比如后续会修改 name),就不该实现
__hash__,否则破坏哈希不变性;此时应设__hash__ = None,明确禁止放入 set 或 dict
避免用 __dict__ 或 vars() 做通用比较
看似省事,但容易出错:比如父类属性、动态添加的字段、描述符或 property 可能不包含在 __dict__ 中,导致本该相等的对象被判为不同。
- 推荐显式列出参与比较的字段,例如
return self.id == other.id and self.name == other.name - 类型检查用
isinstance(other, self.__class__),比type(other) is type(self)更安全(支持继承) - 若字段含可变容器(如 list、dict),需递归或转为不可变形式(如 tuple、frozenset)再参与哈希计算
处理嵌套对象时保持一致性
如果对象属性本身也是自定义类,那它的类也得正确定义 __eq__ 和 __hash__。否则外层对象的比较会退回到引用比较,失去“深层”意义。
- 例如
class Order包含self.items: List[Item],那么Item类必须可哈希且正确实现相等判断 - 若
items是列表,建议存为tuple或用frozenset(如果顺序不重要),便于稳定哈希 - 避免在
__hash__中调用可能引发副作用或依赖外部状态的方法
验证是否生效的简单方法
别只靠 print 看结果,用实际插入行为来验证:
- 创建两个内容完全相同的实例,放进同一个 set,看最终长度是不是 1
- 用
in操作测试成员判断:obj1 in {obj2}应返回True(前提是 obj1 == obj2 且 hash(obj1) == hash(obj2)) - 打印
hash(obj1), hash(obj2)和obj1 == obj2,三者必须同时满足预期

















