web-dev-qa-db-fra.com

Python! = Opération vs "n'est pas"

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 était la différence et pourquoi l'une d'elles pouvait être recommandée plutôt que l'autre?

214
viksit

== est un test d'égalité. Il vérifie si les côtés droit et gauche sont des objets égaux (en fonction de leurs méthodes __eq__ ou __cmp__.)

is est 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'opération is.

Vous utilisez is (et is not) pour des singletons, comme None, où vous ne vous souciez pas des objets pouvant prétendre être None ou de l'endroit souhaité. protège contre la casse d'objets lors de la comparaison avec None.

264
Thomas Wouters

Tout d'abord, laissez-moi passer en revue quelques termes. Si vous souhaitez seulement que votre question soit répondue, faites défiler jusqu'à "Répondre à votre question".

Définitions

Identité d'objet: Lorsque vous créez un objet, vous pouvez l'affecter à une variable. Vous pouvez alors aussi 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, close et dismiss désignent tous le même objet en mémoire. Vous n'avez créé qu'un seul objet Button et les trois variables font référence à cet objet. Nous disons que cancel, close et dismiss désignent tous 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 au même objet exact en mémoire . Avec l'égalité des objets, vous pouvez définir vos propres règles pour la comparaison de deux objets. Lorsque vous écrivez if a == b:, vous dites essentiellement if a.__eq__(b):. Cela vous permet de définir une méthode __eq__ sur a afin 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 standard sans en créer de nouvelles en mémoire.

Ici, j'ai deux chaînes unicode, a et 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 de manière égale. Ce qui se passe ici, c'est que l'objet unicode a implémenté la méthode __eq__.

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__ sur unicode est définitivement mis en œuvre 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, a et 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 de manière égale. Ce qui se passe ici, c'est que l'objet Monitor a implémenté la méthode __eq__.

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 dans Python - il n'y a jamais qu'une seule instance en mémoire.

En comparant 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 rechercher si votre objet dispose d'une méthode __eq__. Si ce n'est pas le cas, il examine chaque super-classe à la recherche d'une méthode __eq__. S'il en trouve un, Python l'appelle. Cela est particulièrement grave si la méthode __eq__ est lente et ne retourne pas immédiatement lorsqu'elle remarque que l'autre objet est None.

N'avez-vous pas implémenté __eq__? Ensuite, Python trouvera probablement la méthode __eq__ sur object et l'utilisera à la place, qui vérifie simplement l'identité de l'objet.

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

128
Wesley

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)
27
Alok Singhal

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

16
 >>> () est () 
 Vrai 
 >>> 1 est 1 
 Vrai 
 >>> (1,) == (1,) 
 Vrai 
 >>> (1,) est (1,) 
 Faux 
 >>> a = (1,) 
 >>> b = a 
 >>> a est b 
 Vrai 

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

7
ephemient