J'écris une réponse mise à jour pour Python 3 à cette question.
Comment est __eq__
géré en Python et dans quel ordre?
a == b
Il est généralement entendu, mais pas toujours le cas, qui a == b
invoque a.__eq__(b)
, ou type(a).__eq__(a, b)
.
Explicitement, l'ordre d'évaluation est:
- si
b
le type de s est une sous-classe stricte (pas du même type) du a
type de s et a un __eq__
, appelez-le et renvoyez la valeur si la comparaison est implémentée,
- Sinon, si
a
a __eq__
, appelez et le retourner si la comparaison est mise en œuvre,
- sinon, voyez si nous n'avons pas appelé b's
__eq__
et qu'il l'a, puis appelez-le et retournez-le si la comparaison est implémentée,
- sinon, enfin, faites la comparaison pour l'identité, la même comparaison que
is
.
Nous savons si une comparaison n'est pas implémentée si la méthode retourne NotImplemented
.
(Dans Python 2, il y avait une __cmp__
méthode qui a été recherchée, mais elle était obsolète et supprimée dans Python 3.)
Testons nous-mêmes le comportement du premier contrôle en laissant B sous-classe A, ce qui montre que la réponse acceptée est fausse sur ce compte:
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
qui ne s'imprime B __eq__ called
qu'avant le retour False
.
Comment connaissons-nous cet algorithme complet?
Les autres réponses ici semblent incomplètes et obsolètes, je vais donc mettre à jour les informations et vous montrer comment vous pouvez rechercher cela par vous-même.
Ceci est géré au niveau C.
Nous devons examiner deux bits de code différents ici - le code par défaut __eq__
pour les objets de classe object
et le code qui recherche et appelle la __eq__
méthode, qu'elle utilise la méthode par défaut __eq__
ou personnalisée.
Défaut __eq__
La recherche __eq__
dans les documents pertinents de l'API C nous montre ce qui __eq__
est géré par tp_richcompare
- qui dans la "object"
définition de type dans cpython/Objects/typeobject.c
est défini dans object_richcompare
pour 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;
Donc ici, si self == other
nous retournons True
, sinon nous retournons l' NotImplemented
objet. Il s'agit du comportement par défaut pour toute sous-classe d'objets qui n'implémente pas sa propre __eq__
méthode.
Comment __eq__
est appelé
Ensuite, nous trouvons la documentation de l'API C, la fonction PyObject_RichCompare , qui appelle do_richcompare
.
Ensuite, nous voyons que la tp_richcompare
fonction, créée pour la "object"
définition C est appelée par do_richcompare
, alors regardons cela d'un peu plus près.
Le premier contrôle dans cette fonction concerne les conditions des objets comparés:
- ne sont pas du même type, mais
- le type du second est une sous-classe du type du premier, et
- le type du second a une
__eq__
méthode,
puis appelez la méthode de l'autre avec les arguments permutés, renvoyant la valeur si elle est implémentée. Si cette méthode n'est pas implémentée, nous continuons ...
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);
Ensuite, nous voyons si nous pouvons rechercher la __eq__
méthode à partir du premier type et l'appeler. Tant que le résultat n'est pas NotImplemented, c'est-à-dire qu'il est implémenté, nous le renvoyons.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Sinon, si nous n'avons pas essayé la méthode de l'autre type et qu'elle est là, nous l'essayons, et si la comparaison est implémentée, nous la renvoyons.
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);
}
Enfin, nous obtenons une solution de secours au cas où elle ne serait pas implémentée pour l'un ou l'autre type.
Le repli vérifie l'identité de l'objet, c'est-à-dire s'il s'agit du même objet au même endroit en mémoire - c'est le même contrôle que pour 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;
Conclusion
Dans une comparaison, nous respectons d'abord l'implémentation de la sous-classe de la comparaison.
Ensuite, nous tentons la comparaison avec l'implémentation du premier objet, puis avec la seconde s'il n'a pas été appelé.
Enfin, nous utilisons un test d'identité pour comparer l'égalité.