Python! = Opération vs «n'est pas»

250

Dans un commentaire sur cette question , j'ai vu une déclaration qui recommandait d'utiliser

result is not None

contre

result != None

Je me demandais quelle est la différence et pourquoi l'une pourrait être recommandée par rapport à l'autre?

viksit
la source
1
Hmm. Bien que la réponse aux deux questions soit le même concept, je pense que les votes positifs et les réponses détaillées ici contribuent indépendamment au concept de test d'identité et d'égalité.
viksit

Réponses:

301

==est un test d'égalité . Il vérifie si le côté droit et le côté gauche sont des objets égaux (selon leur __eq__ou leurs __cmp__méthodes.)

isest un test d'identité . Il vérifie si le côté droit et le côté gauche sont le même objet. Aucun appel de méthode n'est effectué, les objets ne peuvent pas influencer l' isopération.

Vous utilisez is(et is not) pour les singletons, par exemple None, lorsque vous ne vous souciez pas des objets qui pourraient prétendre être Noneou lorsque vous souhaitez vous protéger contre la rupture d'objets lors de la comparaison None.

Thomas Wouters
la source
3
Merci pour la réponse - pourriez-vous élaborer sur les situations où un objet peut se briser, comparé à Aucun?
viksit
3
@viksit. Nonea peu de méthodes et presque aucun attribut. Si votre __eq__test attendait une méthode ou un attribut, il pourrait casser. def __eq__( self, other ): return self.size == other.size. Par exemple, se cassera s'il otherse trouve None.
S.Lott
36
Ma façon préférée de comprendre cela est la suivante: Python isest comme Java ==. Python ==est comme Java .equals(). Bien sûr, cela n'aide que si vous connaissez Java.
MatrixFrog
4
@MatrixFrog: En PHP ou JavaScript, nous dirions que isc'est comme ===(très égal), et inversement is notc'est comme !==(pas exactement égal).
Orwellophile
3
Est-ce is notqu'un seul opérateur ou est-ce simplement en train de nier le résultat de l' isintérieur comme not foo is bar?
Asad Moosvi
150

Tout d'abord, permettez-moi de passer en revue quelques termes. Si vous souhaitez simplement obtenir une réponse à votre question, faites défiler la page jusqu'à "Répondre à votre question".

Définitions

Identité de l'objet : lorsque vous créez un objet, vous pouvez l'affecter à une variable. Vous pouvez ensuite également l'affecter à une autre variable. Et un autre.

>>> button = Button()
>>> cancel = button
>>> close = button
>>> dismiss = button
>>> print(cancel is close)
True

Dans ce cas, cancel, closeet dismisstous se réfèrent au même objet en mémoire. Vous avez créé un seul Buttonobjet et les trois variables font référence à cet objet unique. Nous disons que cancel, closeet dismisstous se réfèrent à des objets identiques ; c'est-à-dire qu'ils se réfèrent à un seul objet.

Égalité d'objet : lorsque vous comparez deux objets, vous ne vous souciez généralement pas qu'il se réfère exactement au même objet en mémoire. Avec l'égalité des objets, vous pouvez définir vos propres règles de comparaison entre deux objets. Lorsque vous écrivez if a == b:, vous dites essentiellement if a.__eq__(b):. Cela vous permet de définir une __eq__méthode aafin que vous puissiez utiliser votre propre logique de comparaison.

Justification des comparaisons d'égalité

Justification: deux objets ont exactement les mêmes données, mais ne sont pas identiques. (Ils ne sont pas le même objet en mémoire.) Exemple: chaînes

>>> greeting = "It's a beautiful day in the neighbourhood."
>>> a = unicode(greeting)
>>> b = unicode(greeting)
>>> a is b
False
>>> a == b
True

Remarque: j'utilise des chaînes unicode ici car Python est suffisamment intelligent pour réutiliser des chaînes régulières sans en créer de nouvelles en mémoire.

Ici, j'ai deux chaînes unicode, aet b. Ils ont exactement le même contenu, mais ils ne sont pas le même objet en mémoire. Cependant, lorsque nous les comparons, nous voulons qu'ils se comparent égaux. Ce qui se passe ici, c'est que l'objet unicode a implémenté la __eq__méthode.

class unicode(object):
    # ...

    def __eq__(self, other):
        if len(self) != len(other):
            return False

        for i, j in zip(self, other):
            if i != j:
                return False

        return True

Remarque: __eq__on unicodeest définitivement implémenté plus efficacement que cela.

Justification: Deux objets ont des données différentes, mais sont considérés comme le même objet si certaines données clés sont identiques. Exemple: la plupart des types de données de modèle

>>> import datetime
>>> a = Monitor()
>>> a.make = "Dell"
>>> a.model = "E770s"
>>> a.owner = "Bob Jones"
>>> a.warranty_expiration = datetime.date(2030, 12, 31)
>>> b = Monitor()
>>> b.make = "Dell"
>>> b.model = "E770s"
>>> b.owner = "Sam Johnson"
>>> b.warranty_expiration = datetime.date(2005, 8, 22)
>>> a is b
False
>>> a == b
True

Ici, j'ai deux moniteurs Dell aet b. Ils ont la même marque et le même modèle. Cependant, ils n'ont ni les mêmes données ni le même objet en mémoire. Cependant, lorsque nous les comparons, nous voulons qu'ils se comparent égaux. Ce qui se passe ici, c'est que l'objet Monitor a implémenté la __eq__méthode.

class Monitor(object):
    # ...

    def __eq__(self, other):
        return self.make == other.make and self.model == other.model

Répondre à votre question

Lorsque vous comparez à None, utilisez toujours is not. Aucun n'est un singleton en Python - il n'y en a jamais qu'une seule en mémoire.

En comparant l' identité , cela peut être effectué très rapidement. Python vérifie si l'objet auquel vous faites référence a la même adresse mémoire que l'objet global None - une comparaison très, très rapide de deux nombres.

En comparant l' égalité , Python doit vérifier si votre objet a une __eq__méthode. Si ce n'est pas le cas, il examine chaque superclasse à la recherche d'une __eq__méthode. S'il en trouve un, Python l'appelle. Cela est particulièrement mauvais si la __eq__méthode est lente et ne revient pas immédiatement lorsqu'elle remarque que l'autre objet l'est None.

N'avez-vous pas mis en œuvre __eq__? Ensuite, Python trouvera probablement la __eq__méthode objectet l'utilisera à la place - qui vérifie simplement l'identité de l'objet de toute façon.

Lorsque vous comparez la plupart des autres choses en Python, vous utiliserez !=.

Wesley
la source
42

Considérer ce qui suit:

class Bad(object):
    def __eq__(self, other):
        return True

c = Bad()
c is None # False, equivalent to id(c) == id(None)
c == None # True, equivalent to c.__eq__(None)
Alok Singhal
la source
1
Ceci est un exemple très utile et simple. Je vous remercie.
msarafzadeh
18

Noneest un singleton, donc la comparaison d'identité fonctionnera toujours, alors qu'un objet peut simuler la comparaison d'égalité via .__eq__().

Ignacio Vazquez-Abrams
la source
Ah intéressant! Dans quelles situations peut-on vouloir simuler la comparaison de l'égalité entre les deux? Je suppose que cela a des répercussions sur la sécurité d'une manière ou d'une autre.
viksit
1
Il ne s'agit pas de simuler l'égalité, mais de la mettre en œuvre . Il existe de nombreuses raisons de vouloir définir comment un objet se compare à un autre.
Thomas Wouters
1
Je dirais que c'est davantage des implications de confusion que des implications de sécurité.
Greg Hewgill
2
Je n'ai pas rencontré de raison de contrefaire l'égalité None, mais un comportement incorrect concernant Nonepourrait se produire comme effet secondaire de la mise en œuvre de l'égalité contre d'autres types. Ce ne sont pas tant des implications de sécurité que des implications de justesse.
Ignacio Vazquez-Abrams
Ah comme ça, je vois. Merci pour la clarification.
viksit
10
>>> () est ()
Vrai
>>> 1 est 1
Vrai
>>> (1,) == (1,)
Vrai
>>> (1,) est (1,)
Faux
>>> a = (1,)
>>> b = a
>>> a est b
Vrai

Certains objets sont singletons, et donc isavec eux est équivalent à ==. La plupart ne le sont pas.

éphémère
la source
4
La plupart d'entre eux ne fonctionnent que par coïncidence / détail de mise en œuvre. ()et 1ne sont pas intrinsèquement singletons.
Mike Graham
1
Dans l'implémentation de CPython, les petits entiers ( -NSMALLNEGINTS <= n <= NSMALLPOSINTS) et les tuples vides sont des singletons. En effet, ce n'est ni documenté ni garanti, mais il est peu probable qu'il change.
éphémère
3
C'est la façon dont il est mis en œuvre, mais ce n'est pas significatif, utile ou éducatif.
Mike Graham
1
Et en particulier, CPython n'est pas la seule implémentation de Python. Se fier à un comportement qui peut varier selon les implémentations de Python me semble généralement être une mauvaise idée ™.
me_and