==
运算符的本质
当您编写 a == b
时,实际上是在调用一个魔术方法(magic method),也称为 dunder 方法(函数名的两边的都有下划线,the double-underscore)。日常学习中你看能已经见过一些魔术方法,例如:
__init__
方法:在初始化 Python 类的实例时调用__str__
方法:使用str
函数时调用,例如str(xxx)
__repr__
方法,类似于__str__
但在其他情况下也会调用,例如错误消息
魔术方法只是 Python 类上的方法,双下划线表示它们与内置的 Python 函数交互。例如,在 Python 类上重写 __str__
方法会改变 str 函数在该修改类的实例上调用时的行为方式。
当谈到 == 和 __eq__
时,通过一些例子最容易理解。让我们来看看!
重写 __eq_
方法
下面,我们用自己的 __eq__
方法定义一个类。每个 __eq__
方法都有两个参数:
- self,所讨论的类的实例
- 第二个参数(在下面的代码片段中,我们定义为 other),它是与 self 进行比较的任何对象。
class MyEqualClass: def __eq__(self, other): return self is other
然后进行测试:
>>> class MyEqualClass: def __eq__(self, other): return self is other >>> a = MyEqualClass() >>> a == 'Hello World' False >>>
然后这会调用 MyEqualClass 的 __eq__
方法,其中 a 作为 self 参数,字符串 'Hello World'
作为另一个参数。
在上面的例子中,MyClass.__eq__
使用了 is 比较器,相当于比较每个对象的 id。这实际上是 Python 中任何用户定义类的默认行为。
如果我们想自定义一个类,所以对比的对象都相等,就可以这样写:
class MyAlwaysTrueClass: def __init__(self, name): self.name = name def __eq__(self, other): return True
因为我们重写了 __eq__
方法以始终返回 True,这意味着在 == 比较器下,此类的所有实例都将被视为相等,即使它们的名称具有不同的值!
>>> jane = MyAlwaysTrueClass("Jane") >>> bob = MyAlwaysTrueClass("Bob") >>> jane.name == bob.name False >>> jane == bob True
此外,与不同类型的对象相比,我们还可以看到 MyAlwaysTrueClass 的实例表现得很奇怪:
>>> jane = MyAlwaysTrueClass("Jane") >>> jane == "some string" True >>> jane == None True >>> jane == True True
当将 MyAlwaysTrueClass 的实例与任何东西(不仅仅是同一个类的实例)进行比较时,它将返回 True。这是因为,正如我们之前所说,__eq__
方法默认情况下不检查被比较对象的类型。
那如果我们写一个功能相反的 __eq__
的 MyAlwaysFalseClass 呢?
class MyAlwaysFalseClass: def __init__(self, name): self.name = name def __eq__(self, other): return False
你可能觉得这很合理,但是:
>>> class MyAlwaysFalseClass: def __init__(self, name): self.name = name def __eq__(self, other): return False >>> a = MyAlwaysFalseClass("name") >>> a == a False
上述的列子,a == a
的结果是 False,那是因为我们在任何类型作对比中都会返回 Flase
。
此外,由于 __eq__
的行为取决于哪个对象是 self 哪个是 other,我们可以通过使用 MyAlwaysTrueClass 的一个实例和一个 MyAlwaysFalseClass 的实例来获得以下行为:
>>> jane = MyAlwaysTrueClass("Jane") >>> bob = MyAlwaysFalseClass("Bob") >>> jane == bob True >>> bob == jane False >>>
这是因为我们比较 jane 和 bob 的顺序很重要。当我们执行 jane == bob
时,我们使用 MyAlwaysTrueClass 中定义的 __eq__
方法:jane 是 self 而 bob 是 other,并且该方法总是返回 True。当我们写 bob == jane
时,情况正好相反,所以我们得到该表达式的 False 值。
总而言之:魔术方法是一种有趣的方式,它可以让 Python 做一些看起来很奇怪的事情,并且应该自行承担修改的风险!
4 总结
所以我们学了什么?我们已经介绍了 is
和 ==
这两个 Python 中的相等概念,并了解到
is
的本质使用对象 id;==
使用__eq__
魔术方法。
我们还探索了如何覆盖 __eq__
来自定义相等概念,并了解为什么这样做会导致一些奇怪的行为。