129 votes

Comment __eq__ est-il traité en Python et dans quel ordre ?

Puisque Python ne fournit pas de versions gauche/droite de ses opérateurs de comparaison, comment décide-t-il de la fonction à appeler ?

class A(object):
    def __eq__(self, other):
        print "A __eq__ called"
        return self.value == other
class B(object):
    def __eq__(self, other):
        print "B __eq__ called"
        return self.value == other

>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False

Cela semble appeler à la fois __eq__ fonctions.

Je cherche l'arbre de décision officiel.

156voto

Ned Batchelder Points 128913

En a == b invoque l'expression A.__eq__ puisqu'il existe. Son code comprend self.value == other . Comme les int ne savent pas comment se comparer aux B, Python essaie d'invoquer B.__eq__ pour voir s'il sait se comparer à un int.

Si vous modifiez votre code pour indiquer les valeurs comparées :

class A(object):
    def __eq__(self, other):
        print("A __eq__ called: %r == %r ?" % (self, other))
        return self.value == other
class B(object):
    def __eq__(self, other):
        print("B __eq__ called: %r == %r ?" % (self, other))
        return self.value == other

a = A()
a.value = 3
b = B()
b.value = 4
a == b

il s'imprimera :

A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?

0 votes

Également pertinent dans Python3 pour ceux qui se posent la question.

73voto

kev Points 41855

Lorsque Python2.x voit a == b il tente ce qui suit.

  • Si type(b) est une classe de style nouveau, et type(b) est une sous-classe de type(a) y type(b) a remplacé __eq__ Le résultat est alors b.__eq__(a) .
  • Si type(a) a remplacé __eq__ (c'est-à-dire, type(a).__eq__ n'est pas object.__eq__ ), le résultat est alors a.__eq__(b) .
  • Si type(b) a remplacé __eq__ Le résultat est alors b.__eq__(a) .
  • Si aucun des éléments ci-dessus n'est présent, Python répète le processus à la recherche de __cmp__ . S'il existe, les objets sont égaux s'il renvoie zero .
  • En dernier recours, Python appelle object.__eq__(a, b) que es True si a y b sont le même objet.

Si l'une des méthodes spéciales renvoie NotImplemented Python fait comme si la méthode n'existait pas.

Notez bien cette dernière étape : si ni l'un ni l'autre a ni b surcharges == entonces a == b est identique à a is b .


En https://eev.ee/blog/2012/03/24/python-faq-equality/

43voto

Aaron Hall Points 7381

Changements/mises à jour de Python 3 pour cet algorithme

Comment la __eq__ traitées en Python et dans quel ordre ?

a == b

Il est généralement admis, mais ce n'est pas toujours le cas, que a == b invoque a.__eq__(b) o type(a).__eq__(a, b) .

Explicitement, l'ordre d'évaluation est le suivant :

  1. si b est une sous-classe stricte (pas le même type) de a et possède un __eq__ l'appelle et renvoie la valeur si la comparaison est mise en œuvre,
  2. sinon, si a a __eq__ l'appelle et le renvoie si la comparaison est mise en œuvre,
  3. else, see if we didn't call b's __eq__ et qu'il l'a, puis l'appelle et le renvoie si la comparaison est mise en œuvre,
  4. enfin, faire la comparaison pour l'identité, la même comparaison que celle de is .

Nous savons qu'une comparaison n'est pas mise en œuvre si la méthode renvoie NotImplemented .

(Dans Python 2, il y avait une fonction __cmp__ qui était recherchée, mais elle a été dépréciée et supprimée dans Python 3).

Testons par nous-mêmes le comportement de la première vérification en laissant B sous-classer A, ce qui montre que la réponse acceptée est erronée sur ce point :

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 n'imprime que B __eq__ called avant de renvoyer False .

Notez que je corrige également une petite erreur dans la question où self.value est comparée à other au lieu de other.value - dans cette comparaison, nous obtenons deux objets ( self y other ), généralement du même type puisque nous ne faisons pas de vérification de type ici (mais ils peuvent être de types différents), et nous avons besoin de savoir s'ils sont égaux. Pour savoir s'ils sont égaux ou non, nous vérifions la valeur de la variable value ce qui doit être fait pour les deux objets.

Comment connaître cet algorithme complet ?

Les autres réponses semblent incomplètes et obsolètes, c'est pourquoi je vais mettre à jour les informations. y Je vous montrerai comment vous pouvez faire vos propres recherches.

Cette question est traitée au niveau C.

Nous devons examiner deux éléments de code différents : le code par défaut, le code d'accès et le code de sécurité. __eq__ pour les objets de la classe object et le code qui recherche et appelle la fonction __eq__ qu'elle utilise ou non la méthode par défaut __eq__ ou un modèle personnalisé.

Défaut __eq__

Regarder __eq__ dans le documentation pertinente sur l'api C nous montre que __eq__ est géré par tp_richcompare - qui, dans la "object" définition du 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;

Ainsi, ici, si self == other nous renvoyons True sinon nous renvoyons le NotImplemented objet. C'est le comportement par défaut de toute sous-classe d'objet qui n'implémente pas sa propre fonction __eq__ méthode.

Comment __eq__ est appelé

Nous trouvons ensuite la documentation de l'API C, le document PyObject_RichCompare qui appelle la fonction do_richcompare .

Nous constatons alors que le tp_richcompare créée pour la fonction "object" La définition C est appelée par do_richcompare Nous allons donc examiner cela de plus près.

La première vérification de cette fonction porte sur les conditions des objets comparés :

  • sont no le même type, mais
  • le type du second est une sous-classe du type du premier, et
  • le type du second a un __eq__ méthode,

puis appeler la méthode de l'autre avec les arguments échangés, en renvoyant la valeur si elle a été mise en œuvre. 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 verrons si nous pouvons consulter le __eq__ 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 existe, 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 disposons d'une solution de repli au cas où elle ne serait pas mise en œuvre pour l'un ou l'autre type.

La solution de repli vérifie l'identité de l'objet, c'est-à-dire s'il s'agit du même objet au même endroit dans la mémoire. 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 comparaison.

Nous tentons ensuite la comparaison avec l'implémentation du premier objet, puis avec celle du second si elle n'a pas été appelée.

Enfin, nous utilisons un test d'identité pour comparer l'égalité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X