关于比较:如何在Python中处理__eq__以及以什么顺序处理?

How is __eq__ handled in Python and in what order?

由于python不提供其比较运算符的左/右版本,它如何决定调用哪个函数?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A(object):
    def __eq__(self, other):
        print"A __eq__ called"
        return self.value == other
class B(object):
    def __eq__(self, other):
        print"B __eq__ called"
        return self.value == other

>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False

这似乎调用了两个__eq__函数。只是在寻找官方的决策树。


a == b表达式调用A.__eq__,因为它存在。它的代码包括self.value == other。由于int不知道如何将自己与b进行比较,所以python尝试调用B.__eq__来查看它是否知道如何将自己与int进行比较。

如果修改代码以显示正在比较的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A(object):
    def __eq__(self, other):
        print("A __eq__ called: %r == %r ?" % (self, other))
        return self.value == other
class B(object):
    def __eq__(self, other):
        print("B __eq__ called: %r == %r ?" % (self, other))
        return self.value == other

a = A()
a.value = 3
b = B()
b.value = 4
a == b

它将打印:

1
2
A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?


当python2.x看到a == b时,它尝试以下操作。

  • 如果type(b)是一个新的类,而type(b)type(a)的一个子类,而type(b)已经覆盖了__eq__,则结果为b.__eq__(a)
  • 如果type(a)已经覆盖__eq__(即type(a).__eq__不是object.__eq__,则结果为a.__eq__(b)
  • 如果type(b)已覆盖__eq__,则结果为b.__eq__(a)
  • 如果以上都不是这种情况,那么python会重复寻找__cmp__的过程。如果它存在,当它返回zero时,对象是相等的。
  • 作为最后的回退,python调用object.__eq__(a, b),即Trueiff ab是同一个对象。

如果任何一个特殊的方法返回NotImplemented,那么python就好像该方法不存在一样。

注意最后一步:如果ab都没有超载==,那么a == ba is b相同。

来自https://eev.ee/blog/2012/03/24/python-faq-equality/