Estou escrevendo uma resposta atualizada para Python 3 a esta pergunta.
Como é __eq__
tratado em Python e em que ordem?
a == b
É geralmente entendido, mas nem sempre o caso, que a == b
invoca a.__eq__(b)
, ou type(a).__eq__(a, b)
.
Explicitamente, a ordem de avaliação é:
- if
b
é uma subclasse estrita (não é o mesmo tipo) do a
tipo de e tem um __eq__
, chame-o e retorne o valor se a comparação for implementada,
- senão, se
a
tiver __eq__
, chame-o e retorne-o se a comparação for implementada,
- caso contrário, veja se não chamamos b's
__eq__
e ele tem, então chame e retorne se a comparação for implementada,
- senão, finalmente, faça a comparação para identidade, a mesma comparação que
is
.
Sabemos se uma comparação não é implementada se o método retornar NotImplemented
.
(No Python 2, havia um __cmp__
método que foi procurado, mas foi descontinuado e removido no Python 3.)
Vamos testar o comportamento da primeira verificação por nós mesmos, deixando B subclasse A, o que mostra que a resposta aceita está errada nesta contagem:
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 só imprime B __eq__ called
antes de retornar False
.
Como sabemos esse algoritmo completo?
As outras respostas aqui parecem incompletas e desatualizadas, portanto, atualizarei as informações e mostrarei como você pode verificar isso por si mesmo.
Isso é tratado no nível C.
Precisamos examinar dois bits diferentes de código aqui - o padrão __eq__
para objetos de classe object
e o código que procura e chama o __eq__
método, independentemente de usar o padrão __eq__
ou personalizado.
Padrão __eq__
Olhando __eq__
-se nos docs relevantes API C mostra-nos que __eq__
é tratado por tp_richcompare
- que na "object"
definição do tipo de cpython/Objects/typeobject.c
é definida em object_richcompare
para 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;
Portanto, aqui, se self == other
retornarmos True
, retornaremos o NotImplemented
objeto. Este é o comportamento padrão para qualquer subclasse de objeto que não implementa seu próprio __eq__
método.
Como __eq__
é chamado
Em seguida, encontramos os documentos da API C, a função PyObject_RichCompare , que chama do_richcompare
.
Então, vemos que a tp_richcompare
função criada para a "object"
definição C é chamada por do_richcompare
, então vamos examinar isso um pouco mais de perto.
A primeira verificação nesta função é para as condições dos objetos sendo comparados:
- não são do mesmo tipo, mas
- o tipo do segundo é uma subclasse do tipo do primeiro, e
- o segundo tipo tem um
__eq__
método,
em seguida, chame o método do outro com os argumentos trocados, retornando o valor se implementado. Se esse método não for implementado, 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);
Em seguida, veremos se podemos pesquisar o __eq__
método do primeiro tipo e chamá-lo. Desde que o resultado não seja NotImplemented, ou seja, ele seja implementado, nós o retornamos.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Do contrário, se não tentamos o método do outro tipo e ele está lá, então tentamos e, se a comparação for implementada, a retornamos.
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);
}
Por fim, obtemos um fallback caso não seja implementado para nenhum dos tipos.
O substituto verifica a identidade do objeto, ou seja, se é o mesmo objeto no mesmo lugar na memória - esta é a mesma verificação de 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;
Conclusão
Em uma comparação, respeitamos a implementação da subclasse de comparação primeiro.
Em seguida, tentamos a comparação com a implementação do primeiro objeto e, em seguida, com a do segundo caso não tenha sido chamado.
Finalmente, usamos um teste de identidade para comparação de igualdade.