При попытке построить пример, в котором a == b не совпадает с b == a, кажется, что я случайно создал пример, в котором a == b не совпадает с a.__eq__(b):
class A:
def __eq__(self, other: object) -> bool:
return type(other) is A
class B(A):
pass
if __name__ == '__main__':
a = A()
b = B()
assert not a.__eq__(b) # as expected
assert not (a == b) # Why does this fail?
Может ли кто-нибудь объяснить мне, почему последнее утверждение не работает? Я ожидал, что он будет таким же, как и второй.
Отвечает ли это на ваш вопрос? Есть ли разница между «= = " и «есть»?
@toyotaSupra Я подозреваю, что они уже знают разницу, они проводили эксперимент.
@toyotaSupra Нет, если честно, это не так.
@mousetail Просто чтобы убедиться, что я понял ваш комментарий: вы утверждаете, что a == b оценивается как a.__eq__(b) or b.__eq__(a)? Если да, есть ли у вас ссылка?
@SebastianThomas Кажется, это особый случай, если один класс является подклассом другого, тогда будут использоваться только подклассы __eq__
@mousetail Но B.__eq__ — это то же самое, что A.__eq__, не так ли?
Да, но сейчас other есть a, что типа A делает __eq__ Правда.
@SebastianThomas Да, но (см. ответ Matszwecja) аргументы перевернуты. a == b, поскольку B является подклассом A, реализуется как b.__eq__(a), а не a.__eq__(b).
Кстати, равенство должно быть симметричным; не давайте ему определения, чувствительного к порядку аргументов (и, конечно же, не игнорируйте self в определении).
@chepner Спасибо, я это знаю. Я пытался явно дать бессмысленное определение, которое должно было быть несимметричным.






Если вы дадите B собственную версию __eq__() и поместите в каждую операторы печати, легко продемонстрировать, что a == b на самом деле оценивается как b.__eq__(a). Нет, я не знаю, почему сделано совсем не так, как я наивно ожидал.
Кажется, это происходит только в том случае, если B является подклассом
Соответствующая цитата, объясняющая происходящее, находится в документации:
Если операнды имеют разные типы и тип правого операнда является прямым или косвенным подклассом типа левого операнда, отраженный метод правого операнда имеет приоритет, в противном случае приоритет имеет метод левого операнда. Виртуальное подклассирование не рассматривается.
В частности, a == b превращается в b.__eq__(a), а не в a.__eq__(b).
Когда сравниваются два объекта в Python, интерпретатор Python использует операцию do_richcompare, написанную на C. См. здесь:
https://github.com/python/cpython/blob/v3.8.3/Objects/object.c#L717
В коде C одна из первых проверок предназначена для подтипирования.
do_richcompare(PyObject *v, PyObject *w, int op)
{
richcmpfunc f;
PyObject *res;
int checked_reverse_op = 0;
if (v->ob_type != w->ob_type &&
PyType_IsSubtype(w->ob_type, v->ob_type) &&
(f = w->ob_type->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
По сути, это говорит о том, что если вы используете сравнение:
v == w
Но тип w является подклассом типа v, тогда верните
w == v
что эквивалентно w.__eq__(v)
Или в этом конкретном случае вызов:
a == b
вернется
b == a
потому что B это подкласс A
Потому что
b.__eq__(a)