Поскольку Python не предоставляет левую / правую версии своих операторов сравнения, как он решает, какую функцию вызывать?
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__
функции.
Я ищу официальное дерево решений.
источник
__eq__
только для экземпляра некоторого типа, которого недостаточно для переопределения ==?Я пишу обновленный ответ на этот вопрос для Python 3.
Обычно понимается, но не всегда, когда
a == b
вызываетсяa.__eq__(b)
, илиtype(a).__eq__(a, b)
.Явно порядок оценки следующий:
b
тип является строгим подклассом (не того же типа)a
типа и имеет__eq__
, вызовите его и верните значение, если сравнение реализовано,a
есть__eq__
, вызовите его и верните, если сравнение реализовано,__eq__
и он есть, затем вызовите и верните его, если сравнение реализовано,is
.Мы знаем, не реализовано ли сравнение, если метод возвращает
NotImplemented
.(В Python 2
__cmp__
искали метод, но он устарел и был удален в Python 3.)Давайте проверим поведение первой проверки для себя, допустив B подкласс A, который показывает, что принятый ответ неверен в этом отношении:
class A: value = 3 def __eq__(self, other): print('A __eq__ called') return self.value == other.value class B(A): value = 4 def __eq__(self, other): print('B __eq__ called') return self.value == other.value a, b = A(), B() a == b
который только печатает
B __eq__ called
перед возвратомFalse
.Откуда нам знать этот полный алгоритм?
Другие ответы здесь кажутся неполными и устаревшими, поэтому я собираюсь обновить информацию и показать вам, как вы можете найти это самостоятельно.
Это обрабатывается на уровне C.
Здесь нам нужно рассмотреть два разных фрагмента кода - значение
__eq__
по умолчанию для объектов классаobject
и код, который ищет и вызывает__eq__
метод независимо от того, использует ли он значение по умолчанию.__eq__
или пользовательский.По умолчанию
__eq__
Поиск
__eq__
в соответствующей документации C api показывает нам, что__eq__
обрабатываетсяtp_richcompare
- который в"object"
определении типа определенcpython/Objects/typeobject.c
вobject_richcompare
forcase Py_EQ:
.case Py_EQ: /* Return NotImplemented instead of False, so if two objects are compared, both get a chance at the comparison. See issue #1393. */ res = (self == other) ? Py_True : Py_NotImplemented; Py_INCREF(res); break;
Итак, здесь, если
self == other
мы вернемсяTrue
, иначе мы вернемNotImplemented
объект. Это поведение по умолчанию для любого подкласса объекта, который не реализует свой собственный__eq__
метод.Как
__eq__
называетсяЗатем мы находим документы C API, функцию PyObject_RichCompare , которая вызывает
do_richcompare
.Затем мы видим, что
tp_richcompare
функция, созданная для"object"
определения C, вызываетсяdo_richcompare
, поэтому давайте посмотрим на это немного подробнее.Первая проверка в этой функции - условия сравниваемых объектов:
__eq__
метод,затем вызовите другой метод с замененными аргументами, возвращая значение, если оно реализовано. Если этот метод не реализован, продолжаем ...
if (!Py_IS_TYPE(v, Py_TYPE(w)) && PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) && (f = Py_TYPE(w)->tp_richcompare) != NULL) { checked_reverse_op = 1; res = (*f)(w, v, _Py_SwappedOp[op]); if (res != Py_NotImplemented) return res; Py_DECREF(res);
Затем мы видим, можем ли мы найти
__eq__
метод первого типа и вызвать его. Пока результат NotImplemented, то есть реализован, мы его возвращаем.if ((f = Py_TYPE(v)->tp_richcompare) != NULL) { res = (*f)(v, w, op); if (res != Py_NotImplemented) return res; Py_DECREF(res);
Иначе, если мы не пробовали метод другого типа, а он есть, мы затем пробуем его, и если сравнение реализовано, мы возвращаем его.
if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) { res = (*f)(w, v, _Py_SwappedOp[op]); if (res != Py_NotImplemented) return res; Py_DECREF(res); }
Наконец, мы получаем запасной вариант, если он не реализован ни для одного из типов.
Резервная копия проверяет идентичность объекта, то есть является ли это тем же объектом в том же месте в памяти - это та же проверка, что и для
self is other
:/* If neither object implements it, provide a sensible default for == and !=, but raise an exception for ordering. */ switch (op) { case Py_EQ: res = (v == w) ? Py_True : Py_False; break;
Вывод
При сравнении мы в первую очередь уважаем реализацию подкласса сравнения.
Затем мы пытаемся сравнить с реализацией первого объекта, а затем со вторым, если он не был вызван.
Наконец, мы используем тест на идентичность для сравнения на равенство.
источник