Estoy escribiendo una respuesta actualizada para Python 3 a esta pregunta.
¿Cómo se __eq__
maneja en Python y en qué orden?
a == b
Generalmente se entiende, pero no siempre el caso, que a == b
invoca a.__eq__(b)
, otype(a).__eq__(a, b)
.
Explícitamente, el orden de evaluación es:
- si
b
el tipo de es una subclase estricta (no el mismo tipo) de a
tipo de y tiene __eq__
, llámelo y devuelva el valor si la comparación está implementada
- de lo contrario, si
a
tiene __eq__
, llámelo y devuélvalo si se implementa la comparación,
- de lo contrario, vea si no llamamos b
__eq__
y lo tiene, luego llámelo y devuélvalo si la comparación está implementada,
- de lo contrario, finalmente, haga la comparación de identidad, la misma comparación que
is
.
Sabemos si no se implementa una comparación si el método regresa NotImplemented
.
(En Python 2, __cmp__
se buscó un método, pero quedó obsoleto y se eliminó en Python 3).
Probemos el comportamiento de la primera verificación por nosotros mismos dejando que B sea subclase A, lo que muestra que la respuesta aceptada es incorrecta en este aspecto:
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
que solo imprime B __eq__ called
antes de regresar False
.
¿Cómo conocemos este algoritmo completo?
Las otras respuestas aquí parecen incompletas y desactualizadas, así que actualizaré la información y le mostraré cómo podría buscar esto por sí mismo.
Esto se maneja en el nivel C.
Necesitamos mirar dos bits de código diferentes aquí: el predeterminado __eq__
para objetos de clase object
y el código que busca y llama al __eq__
método independientemente de si usa el predeterminado __eq__
o uno personalizado.
Defecto __eq__
Al __eq__
buscar en los documentos relevantes de la api de C , se nos muestra que __eq__
es manejado por tp_richcompare
, que en la "object"
definición de tipo cpython/Objects/typeobject.c
se define en object_richcompare
for 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 #1393. */
res = (self == other) ? Py_True : Py_NotImplemented;
Py_INCREF(res);
break;
Entonces aquí, si self == other
regresamos True
, de lo contrario devolvemos el NotImplemented
objeto. Este es el comportamiento predeterminado para cualquier subclase de objeto que no implemente su propio __eq__
método.
Como __eq__
se llama
Luego encontramos los documentos de la API C, PyObject_RichCompare función , que llama do_richcompare
.
Luego vemos que la tp_richcompare
función, creada para el"object"
definición de C, es llamada por do_richcompare
, así que veamos eso un poco más de cerca.
La primera verificación en esta función es para las condiciones de los objetos que se comparan:
- son no del mismo tipo, pero
- el tipo del segundo es una subclase del tipo del primero, y
- el tipo del segundo tiene un
__eq__
método,
luego llame al método del otro con los argumentos intercambiados, devolviendo el valor si se implementa. Si ese método no se implementa, continuamos ...
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);
A continuación, vemos si podemos buscar el __eq__
método del primer tipo y llamarlo. Siempre que el resultado no sea NotImplemented, es decir, esté implementado, lo devolvemos.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
De lo contrario, si no probamos el método del otro tipo y está ahí, lo probamos y, si se implementa la comparación, lo devolvemos.
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);
}
Finalmente, obtenemos una alternativa en caso de que no se implemente para ninguno de los dos tipos.
El respaldo verifica la identidad del objeto, es decir, si es el mismo objeto en el mismo lugar en la memoria; esta es la misma verificación que para 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;
Conclusión
En una comparación, respetamos primero la implementación de la subclase de la comparación.
Luego intentamos la comparación con la implementación del primer objeto, luego con la del segundo si no fue llamado.
Finalmente, usamos una prueba de identidad para comparar la igualdad.