问题背景:
在Python中,通过运算符 '==' 进行对象值的比较,底层调用方法 eq。该方法的默认行为继承自object类,比较对象的身份(对于大多数类来说)。
问题:
当我们希望自定义类的实例能够有意义地进行比较(例如,两个不同的Person对象如果数据相同就视为相等),标准行为是不够的——我们需要明确地定义比较的方式。
解决方案:
要改变 '==' 的行为,需要在自定义类中重写方法 eq。如果类需要可哈希,还需要重写 hash。
示例代码:
class Person: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if not isinstance(other, Person): return NotImplemented return self.name == other.name and self.age == other.age p1 = Person("Ann", 25) p2 = Person("Ann", 25) print(p1 == p2) # True
关键特性:
比较 '==' 对不同类型对象总是对称的吗?
不一定——如果第一个对象返回 NotImplemented,则会调用反向比较。否则可能会导致不对称。
class A: def __eq__(self, other): return True class B: pass print(A() == B()) # True print(B() == A()) # False
如果没有实现 eq,同一用户自定义类的对象会如何比较?
将比较身份 (id)(内存中的身份,而不是属性值)。
class Foo: def __init__(self, x): self.x = x f1 = Foo(5) f2 = Foo(5) print(f1 == f2) # False
可以不定义 hash 而定义 eq 吗?对于应作为字典键的对象?
不行。如果 eq 被修改,建议也定义 hash,否则对象将变为不可哈希的 (在用作键时会出现 TypeError)。
class Foo: def __eq__(self, other): return True # hash 继承自 object,但行为将是不可预知的 # 最好明确指定 __hash__
负面案例
在设计 ValueObject 类时,团队没有意识到只实现了 eq,而没有实现 hash。尝试将对象用作字典中的键或集合中的元素时,抛出了 TypeError 异常。
优点:
快速引入自定义比较。
缺点:
无法在集合和字典中使用对象,调试复杂。
正面案例
在另一个项目中,开发人员明确实现了两个方法—— eq 和 hash,严格遵循规则:如果两个对象在 eq 上相等,它们的哈希值也必须相等。
优点:
哈希结构正常工作,行为一致,易于维护。
缺点:
失去灵活性:值相等的对象在哈希容器中不可区分。