Каков порядок выполнения __eq__, если одна сторона наследует от другой?
Недавно я наткнулся на странное поведение, касающееся порядка __eq__
методов __eq__
, если одна сторона сравнения является объектом, который наследуется от другой.
Я попробовал это в Python 3.7.2 в общеизвестном академическом примере. Обычно, учитывая сравнение на равенство a == b
, я ожидаю, a.__eq__
будет вызван a.__eq__
, а затем b.__eq__
, если первый вызов вернул NotImplemented
. Однако, похоже, что это не так, если a
и b
являются частью одной и той же иерархии классов. Рассмотрим следующий пример:
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
В случае 1 я ожидаю, a.__eq__
будет вызван дважды, и это также то, что я получаю:
Calling A(<__main__.A object at 0x7f8fda95f860>).__eq__
Calling A(<__main__.A object at 0x7f8fda95f860>).__eq__
Однако в случаях 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
В случаях 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
Кажется, что, если сравниваемые объекты связаны друг с другом, __eq__
объекта дочернего класса всегда вычисляется первым. Есть ли более глубокие причины этого поведения? Если так, это где-то задокументировано? PEP 207 не упоминает этот случай, насколько я вижу. Или я что-то упускаю здесь очевидное?
Ответы
Ответ 1
Из официальной документации за __eq__
:
Если операнды имеют разные типы, и тип правого операнда является прямым или косвенным подклассом типа левого операнда, отраженный метод правого операнда имеет приоритет, в противном случае метод левого операнда имеет приоритет.