Я пишу обновленный ответ на этот вопрос для Python 3.
Как __eq__обрабатывается в Python и в каком порядке?
a == b
Обычно понимается, но не всегда, когда a == bвызывается a.__eq__(b), или type(a).__eq__(a, b).
Явно порядок оценки следующий:
- if
bтип является строгим подклассом (не того же типа) aтипа и имеет __eq__, вызовите его и верните значение, если сравнение реализовано,
- иначе, если
aесть __eq__, вызовите его и верните, если сравнение реализовано,
- иначе, посмотрите, не вызвали ли мы b,
__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_richcomparefor case 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
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;
Вывод
При сравнении мы в первую очередь уважаем реализацию подкласса сравнения.
Затем мы пытаемся сравнить с реализацией первого объекта, а затем со вторым, если он не был вызван.
Наконец, мы используем тест на идентичность для сравнения на равенство.