如果一方继承另一方,__ eq__的执行顺序是什么?

And*_*eas 18 python python-3.x

__eq__如果比较的一侧是从另一侧继承的对象,我最近偶然发现了关于方法执行顺序的看似奇怪的行为。

在一个公认的学术示例中,我已经在Python 3.7.2中进行了尝试。通常,在进行相等性比较的情况下,如果第一个调用返回了a == b,我希望a.__eq__首先被b.__eq__调用NotImplemented。但是,如果ab属于同一类层次结构的一部分,则情况似乎并非如此。考虑以下示例:

class A(object):
  def __eq__(self, other):
    print("Calling A({}).__eq__".format(self))
    return NotImplemented

class B(A):
  def __eq__(self, other):
    print("Calling B({}).__eq__".format(self))
    return True

class C(object):
  def __eq__(self, other):
    print("Calling C({}).__eq__".format(self))
    return False

a = A()
b = B()
c = C()

print("a: {}".format(a)) # output "a: <__main__.A object at 0x7f8fda95f860>"
print("b: {}".format(b)) # output "b: <__main__.B object at 0x7f8fda8bcfd0>"
print("c: {}".format(c)) # output "c: <__main__.C object at 0x7f8fda8bcef0>"

a == a # case 1

a == b # case 2.1
b == a # case 2.2

a == c # case 3.1
c == a # case 3.2
Run Code Online (Sandbox Code Playgroud)

在情况1中,我希望a.__eq__两次被调用,这也是我得到的:

Calling A(<__main__.A object at 0x7f8fda95f860>).__eq__
Calling A(<__main__.A object at 0x7f8fda95f860>).__eq__
Run Code Online (Sandbox Code Playgroud)

但是,在情况2.1和2.2中,b.__eq__无论处于比较的哪一侧,总是首先执行:

Calling B(<__main__.B object at 0x7f8fda8bcfd0>).__eq__ # case 2.1
Calling B(<__main__.B object at 0x7f8fda8bcfd0>).__eq__ # case 2.2
Run Code Online (Sandbox Code Playgroud)

然后在情况3.1和3.2中,如我所料,首先再次评估左侧:

Calling A(<__main__.A object at 0x7f8fda95f860>).__eq__ # case 3.1
Calling C(<__main__.C object at 0x7f8fda8bcef0>).__eq__ # case 3.1
Calling C(<__main__.C object at 0x7f8fda8bcef0>).__eq__ # case 3.2
Run Code Online (Sandbox Code Playgroud)

似乎,如果比较的对象彼此相关,__eq__则总是首先评估子类的对象。这种行为背后是否有更深层的推理?如果是这样,这是否记录在某处?据我所知,PEP 207没有提到这种情况。还是我可能在这里错过了明显的东西?

Len*_*mju 18

官方文档中__eq__

如果操作数是不同类型的,并且右操作数的类型是左操作数类型的直接或间接子类,则右操作数的反射方法具有优先级,否则左操作数的方法具有优先级。

  • 是的,这意味着子类无法覆盖__eq__,这会破坏抽象。如果`__eq__`在父类而不是子类上具有优先权,它将创建一些非常“有趣”的多态调度。 (2认同)