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 == binvoque a.__eq__(b), ou type(a).__eq__(a, b).
Explicitement, l'ordre d'évaluation est:
- si
ble type de s est une sous-classe stricte (pas du même type) du atype de s et a un __eq__, appelez-le et renvoyez la valeur si la comparaison est implémentée,
- Sinon, si
aa __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__ calledqu'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 objectet 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.cest défini dans object_richcomparepour 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 == othernous retournons True, sinon nous retournons l' NotImplementedobjet. 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_richcomparefonction, 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é.