Y a-t-il une différence entre «==» et «is»?

630

Mon Google-fu m'a échoué.

En Python, les deux tests d'égalité suivants sont-ils équivalents?

n = 5
# Test one.
if n == 5:
    print 'Yay!'

# Test two.
if n is 5:
    print 'Yay!'

Est-ce vrai pour les objets où vous compareriez des instances (un listmot à dire)?

D'accord, donc ce genre de réponses à ma question:

L = []
L.append(1)
if L == [1]:
    print 'Yay!'
# Holds true, but...

if L is [1]:
    print 'Yay!'
# Doesn't.

Donc, les ==tests isvalent où les tests pour voir s'ils sont le même objet?

Bernard
la source

Réponses:

928

isretournera Truesi deux variables pointent vers le même objet, ==si les objets référencés par les variables sont égaux.

>>> a = [1, 2, 3]
>>> b = a
>>> b is a 
True
>>> b == a
True

# Make a new copy of list `a` via the slice operator, 
# and assign it to variable `b`
>>> b = a[:] 
>>> b is a
False
>>> b == a
True

Dans votre cas, le deuxième test ne fonctionne que parce que Python met en cache de petits objets entiers, ce qui est un détail d'implémentation. Pour les entiers plus grands, cela ne fonctionne pas:

>>> 1000 is 10**3
False
>>> 1000 == 10**3
True

Il en va de même pour les littéraux de chaîne:

>>> "a" is "a"
True
>>> "aa" is "a" * 2
True
>>> x = "a"
>>> "aa" is x * 2
False
>>> "aa" is intern(x*2)
True

Veuillez également consulter cette question .

Torsten Marek
la source
2
Je trouve que: echo 'import sys;tt=sys.argv[1];print(tt is "foo", tt == "foo", id(tt)==id("foo"))'| python3 - foosortie: False True False.
ahuigo
Vous m'avez perdu avec la b = a[:]partie de copie de la liste des opérateurs de tranche, j'ai donc modifié votre réponse pour y ajouter un commentaire. On dirait que je viens d'atteindre le seuil pour ne pas avoir à faire réviser mes modifications avant de les appliquer, alors j'espère que c'est cool avec vous. Quoi qu'il en soit, voici une référence utile pour copier des listes que j'ai rencontrées et que j'ai dû faire référence pour comprendre ce que vous faisiez: stackoverflow.com/a/2612815/4561887
Gabriel Staples
Une autre façon de démontrer la différence est de comparer des objets de types différents, qui ne peuvent bien sûr jamais être le même objet mais toujours comparer égaux lors de l'utilisation ==. Ainsi, 5.0par exemple, est une valeur à virgule flottante, tandis que 5est un entier. Mais 5.0 == 5reviendra toujours Truecar ils représentent la même valeur. En termes de performances et de typage de canard, isest toujours testé par l'interprète en comparant les adresses mémoire de l'opérande, tandis que ==c'est à l'objet de décider s'il se définit comme égal à autre chose.
Bachsau
3
1000 is 10**3correspond à True dans Python 3.7 puisque 10 ** 3 est de type int. Mais donne la valeur 1000 is 1e3False puisque 1e3 est de type float.
Ahmed Fasih
@AhmedFasih Que la vérité soit vraie ou non 1000 is 10**3dépend de l'implémentation et dépend de la pré-évaluation par le compilateur de l'expression 10**3. x=10; 1000 is x**3évalue à False.
chepner
314

Il existe une règle simple pour vous dire quand utiliser ==ou is.

  • ==est pour l' égalité des valeurs . Utilisez-le lorsque vous souhaitez savoir si deux objets ont la même valeur.
  • isest pour l' égalité de référence . Utilisez-le lorsque vous souhaitez savoir si deux références font référence au même objet.

En général, lorsque vous comparez quelque chose à un type simple, vous vérifiez généralement l' égalité des valeurs , vous devez donc l'utiliser ==. Par exemple, l'intention de votre exemple est probablement de vérifier si x a une valeur égale à 2 ( ==), et non pas s'il xfait littéralement référence au même objet que 2.


Autre chose à noter: en raison du fonctionnement de l'implémentation de référence CPython, vous obtiendrez des résultats inattendus et incohérents si vous utilisez par erreur ispour comparer l'égalité de référence sur des entiers:

>>> a = 500
>>> b = 500
>>> a == b
True
>>> a is b
False

C'est à peu près ce à quoi nous nous attendions: aet bont la même valeur, mais ce sont des entités distinctes. Mais qu'en est-il?

>>> c = 200
>>> d = 200
>>> c == d
True
>>> c is d
True

Cela est incompatible avec le résultat précédent. Que se passe t-il ici? Il s'avère que l'implémentation de référence de Python met en cache des objets entiers dans la plage -5..256 en tant qu'instances singleton pour des raisons de performances. Voici un exemple qui le démontre:

>>> for i in range(250, 260): a = i; print "%i: %s" % (i, a is int(str(i)));
... 
250: True
251: True
252: True
253: True
254: True
255: True
256: True
257: False
258: False
259: False

C'est une autre raison évidente de ne pas utiliser is: le comportement est laissé aux implémentations lorsque vous l'utilisez par erreur pour l'égalité des valeurs.

John Feminella
la source
En ce qui concerne le premier exemple de a=500et b=500, je voulais juste souligner que si vous définissez aet bsur un entier entre [-5, 256], a is bretourne en fait True. Plus d'informations ici: stackoverflow.com/q/306313/7571052
AsheKetchum
1
@AsheKetchum, oui, notez que j'ai écrit "Il s'avère que l'implémentation de référence de Python met en cache des objets entiers dans la plage -5..256 en tant qu'instances singleton pour des raisons de performances."
John Feminella
34

==détermine si les valeurs sont égales, tandis que isdétermine si elles sont exactement le même objet.

stephenbayer
la source
32

Y a-t-il une différence entre ==et isen Python?

Oui, ils ont une différence très importante.

==: vérifier l'égalité - la sémantique est que les objets équivalents (qui ne sont pas nécessairement le même objet) seront testés comme égaux. Comme le dit la documentation :

Les opérateurs <,>, ==,> =, <= et! = Comparent les valeurs de deux objets.

is: vérifier l'identité - la sémantique est que l'objet (tel qu'il est conservé en mémoire) est l'objet. Encore une fois, la documentation dit :

Les opérateurs iset is nottestent l'identité de l'objet: x is yest vrai si et seulement si xet ysont le même objet. L'identité de l'objet est déterminée à l'aide de la id()fonction. x is not ydonne la valeur de vérité inverse.

Ainsi, la vérification d'identité est identique à la vérification de l'égalité des ID des objets. C'est,

a is b

est le même que:

id(a) == id(b)

idest la fonction intégrée qui renvoie un entier qui "est garanti unique parmi les objets existants simultanément" (voir help(id)) et où aet btout objet arbitraire.

Autres instructions d'utilisation

Vous devez utiliser ces comparaisons pour leur sémantique. Utilisez ispour vérifier l'identité et ==pour vérifier l'égalité.

Donc, en général, nous utilisons ispour vérifier l'identité. Ceci est généralement utile lorsque nous recherchons un objet qui ne devrait exister qu'une seule fois en mémoire, appelé "singleton" dans la documentation.

Les cas d'utilisation isincluent:

  • None
  • valeurs enum (lors de l'utilisation d'énumérations à partir du module enum)
  • généralement des modules
  • généralement des objets de classe résultant de définitions de classe
  • généralement des objets fonction résultant de définitions de fonctions
  • toute autre chose qui ne devrait exister qu'une seule fois dans la mémoire (tous les singletons, en général)
  • un objet spécifique que vous voulez par identité

Les cas d'utilisation habituels ==incluent:

  • nombres, y compris des entiers
  • cordes
  • listes
  • ensembles
  • dictionnaires
  • objets mutables personnalisés
  • d'autres objets immuables intégrés, dans la plupart des cas

Le cas d'usage général, encore une fois, car ==, est l'objet que vous voulez peut - être pas le même objet, au lieu , il peut être un équivalent d' un

PEP 8 directions

PEP 8, le guide de style Python officiel pour la bibliothèque standard mentionne également deux cas d'utilisation pouris :

Les comparaisons avec des singletons comme Nonedevraient toujours être faites avec isou is not, jamais avec les opérateurs d'égalité.

Aussi, méfiez-vous d'écrire if xquand vous voulez vraiment dire if x is not None- par exemple lorsque vous testez si une variable ou un argument par défaut a None été défini sur une autre valeur. L'autre valeur peut avoir un type (tel qu'un conteneur) qui pourrait être faux dans un contexte booléen!

Déduire l'égalité de l'identité

Si isc'est vrai, l'égalité peut généralement être déduite - logiquement, si un objet est lui-même, il doit alors être testé comme équivalent à lui-même.

Dans la plupart des cas, cette logique est vraie, mais elle repose sur la mise en œuvre de la __eq__méthode spéciale. Comme le disent les docs ,

Le comportement par défaut pour la comparaison d'égalité ( ==et !=) est basé sur l'identité des objets. Par conséquent, la comparaison d'égalité des instances avec la même identité entraîne l'égalité et la comparaison d'égalité des instances avec des identités différentes entraîne l'inégalité. Une motivation pour ce comportement par défaut est le désir que tous les objets soient réflexifs (c'est-à-dire que x est y implique x == y).

et dans un souci de cohérence, recommande:

La comparaison de l'égalité doit être réflexive. En d'autres termes, les objets identiques doivent comparer égaux:

x is y implique x == y

Nous pouvons voir que c'est le comportement par défaut pour les objets personnalisés:

>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)

La contrapositive est également généralement vraie - si quelque chose est différent, vous pouvez généralement déduire qu'il ne s'agit pas du même objet.

Étant donné que les tests d'égalité peuvent être personnalisés, cette inférence n'est pas toujours vraie pour tous les types.

Une exception

Une exception notable est nan- il teste toujours comme différent de lui-même:

>>> nan = float('nan')
>>> nan
nan
>>> nan is nan
True
>>> nan == nan           # !!!!!
False

La vérification de l'identité peut être beaucoup plus rapide que la vérification de l'égalité (ce qui peut nécessiter une vérification récursive des membres).

Mais il ne peut pas être substitué à l'égalité où vous pouvez trouver plus d'un objet comme équivalent.

Notez que la comparaison de l'égalité des listes et des tuples supposera que l'identité des objets est égale (car il s'agit d'une vérification rapide). Cela peut créer des contradictions si la logique est incohérente - comme c'est le cas pour nan:

>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True

Un récit édifiant:

La question tente d'utiliser ispour comparer des entiers. Vous ne devez pas supposer qu'une instance d'un entier est la même instance que celle obtenue par une autre référence. Cette histoire explique pourquoi.

Un commentateur avait du code qui reposait sur le fait que les petits entiers (-5 à 256 inclus) sont des singletons en Python, au lieu de vérifier l'égalité.

Wow, cela peut conduire à des bugs insidieux. J'avais un code qui vérifiait si a est b, qui fonctionnait comme je le voulais parce que a et b sont généralement de petits nombres. Le bogue ne s'est produit qu'aujourd'hui, après six mois de production, car a et b étaient finalement suffisamment gros pour ne pas être mis en cache. - gwg

Cela a fonctionné dans le développement. Il a peut-être réussi certains tests.

Et cela a fonctionné en production - jusqu'à ce que le code vérifie un entier supérieur à 256, moment auquel il a échoué en production.

Il s'agit d'un échec de production qui aurait pu être détecté dans la révision du code ou éventuellement avec un vérificateur de style.

Permettez-moi de souligner: ne pas utiliser ispour comparer des entiers.

Aaron Hall
la source
"ne pas utiliser est du tout" serait aussi une bonne règle. L'idiome is Noneétant une exception, mais cela dit ça == Nonemarche aussi ...
Jean-François Fabre
@ Jean-FrançoisFabre Autre exception: la documentation officielle semble recommander d'utiliser ispour comparer l' Enumart.
Arthur
@Arthur J'ai ajouté une liste de cas d'utilisation ...
Aaron Hall
19

Quelle est la différence entre iset ==?

==et issont une comparaison différente! Comme d'autres l'ont déjà dit:

  • == compare les valeurs des objets.
  • is compare les références des objets.

En Python, les noms font référence aux objets, par exemple dans ce cas value1et value2font référence à une intinstance stockant la valeur 1000:

value1 = 1000
value2 = value1

entrez la description de l'image ici

Parce que value2fait référence au même objet iset ==donnera True:

>>> value1 == value2
True
>>> value1 is value2
True

Dans l'exemple suivant, les noms value1et les value2références à différentes intinstances, même si les deux stockent le même entier:

>>> value1 = 1000
>>> value2 = 1000

entrez la description de l'image ici

Parce que est stockée la même valeur (entier) ==sera True, c'est pourquoi il est souvent appelé « comparaison de la valeur ». Cependant isreviendra Falsecar ce sont des objets différents:

>>> value1 == value2
True
>>> value1 is value2
False

Quand utiliser quoi?

isEst généralement une comparaison beaucoup plus rapide. C'est pourquoi CPython met en cache (ou peut-être réutiliser serait le meilleur terme) certains objets comme les petits entiers, certaines chaînes, etc. Mais cela devrait être traité comme un détail d'implémentation qui pourrait (même si cela est peu probable) changer à tout moment sans avertissement.

Vous ne devez utiliser queis si vous:

  • veulent vérifier si deux objets sont vraiment le même objet (pas seulement la même "valeur"). Un exemple peut être si vous utilisez un objet singleton comme constante.
  • veulent comparer une valeur à une constante Python . Les constantes en Python sont:

    • None
    • True1
    • False1
    • NotImplemented
    • Ellipsis
    • __debug__
    • cours (par exemple int is intou int is float)
    • il pourrait y avoir des constantes supplémentaires dans les modules intégrés ou les modules tiers. Par exemple à np.ma.maskedpartir du module NumPy)

Dans tous les autres cas, vous devez utiliser== pour vérifier l'égalité.

Puis-je personnaliser le comportement?

Il y a un aspect ==qui n'a pas déjà été mentionné dans les autres réponses: il fait partie du "modèle de données" Pythons . Cela signifie que son comportement peut être personnalisé à l'aide de la __eq__méthode. Par exemple:

class MyClass(object):
    def __init__(self, val):
        self._value = val

    def __eq__(self, other):
        print('__eq__ method called')
        try:
            return self._value == other._value
        except AttributeError:
            raise TypeError('Cannot compare {0} to objects of type {1}'
                            .format(type(self), type(other)))

Ceci est juste un exemple artificiel pour illustrer que la méthode est vraiment appelée:

>>> MyClass(10) == MyClass(10)
__eq__ method called
True

Notez que par défaut (si aucune autre implémentation de __eq__ne peut être trouvée dans la classe ou les superclasses) __eq__utilise is:

class AClass(object):
    def __init__(self, value):
        self._value = value

>>> a = AClass(10)
>>> b = AClass(10)
>>> a == b
False
>>> a == a

Il est donc important d'implémenter __eq__si vous voulez "plus" qu'une simple comparaison de références pour les classes personnalisées!

En revanche, vous ne pouvez pas personnaliser les ischèques. Il sera toujours comparé juste si vous avez la même référence.

Ces comparaisons renverront-elles toujours un booléen?

Parce qu'il __eq__peut être réimplémenté ou remplacé, il n'est pas limité à retourner Trueou False. Il pourrait renvoyer n'importe quoi (mais dans la plupart des cas, il devrait renvoyer un booléen!).

Par exemple, avec les tableaux NumPy, le ==renvoie un tableau:

>>> import numpy as np
>>> np.arange(10) == 2
array([False, False,  True, False, False, False, False, False, False, False], dtype=bool)

Mais les ischèques reviendront toujours Trueou False!


1 Comme Aaron Hall l'a mentionné dans les commentaires:

En règle générale, vous ne devriez pas effectuer de vérifications is Trueou is Falseparce que l'on utilise normalement ces «vérifications» dans un contexte qui convertit implicitement la condition en booléen (par exemple dans une ifinstruction). Donc, faire la is Truecomparaison et la distribution booléenne implicite fait plus de travail que simplement faire la distribution booléenne - et vous vous limitez aux booléens (qui n'est pas considéré comme pythonique).

Comme PEP8 le mentionne:

Ne comparez pas les valeurs booléennes à Trueou Falseutilisez ==.

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:
MSeifert
la source
2
Je vais devoir être en désaccord sur votre affirmation de comparer les "constantes" avec is- les noms qui pointent vers des booléens devraient être vérifiés avec un contexte booléen - comme if __debug__:ou if not __debug__:. Vous ne devriez jamais faire if __debug__ is True:ou if __debug__ == True:- en outre, une constante est simplement une valeur sémantique constante, pas un singleton, donc vérifier avec isdans ce cas n'est pas sémantiquement correct. Je vous mets au défi de trouver une source pour étayer vos affirmations - je ne pense pas que vous en trouverez une.
Aaron Hall
@AaronHall Qu'est-ce qui vous fait penser que les constantes ne sont pas des singletons? Notez que seulement None, True, Falseet __debug__sont ce que vous appelez « valeur sémantique constante », parce qu'ils ne peuvent pas être réaffectés. Mais tous sont des singletons.
MSeifert
Lisez PEP 8 - Ctrl-F et recherchez le mot "pire". - Si vous n'aimez pas, vous utiliserez self.assertTrue
Aaron Hall
@AaronHall Dans certaines circonstances, vous avez vraiment besoin de la is Trueou de la if Falsevérification (mais oui, ce sont assez rares - mais si vous les faites, vous pouvez les faire en utilisant is). C'est pourquoi même CPython les utilise parfois (par exemple ici ou ici )
MSeifert
19

Ils sont complètement différents . isvérifie l'identité de l'objet, tandis que ==vérifie l'égalité (une notion qui dépend des types des deux opérandes).

Ce n'est qu'une heureuse coïncidence que " is" semble fonctionner correctement avec de petits nombres entiers (par exemple 5 == 4 + 1). C'est parce que CPython optimise le stockage des entiers dans la plage (-5 à 256) en les faisant singletons . Ce comportement est totalement dépendant de l'implémentation et n'est pas garanti d'être conservé sous toutes sortes d'opérations de transformation mineures.

Par exemple, Python 3.5 crée également des singletons de chaînes courtes, mais leur découpage perturbe ce comportement:

>>> "foo" + "bar" == "foobar"
True
>>> "foo" + "bar" is "foobar"
True
>>> "foo"[:] + "bar" == "foobar"
True
>>> "foo"[:] + "bar" is "foobar"
False
Dan Lenski
la source
6

Votre réponse est correcte. L' isopérateur compare l'identité de deux objets. L' ==opérateur compare les valeurs de deux objets.

L'identité d'un objet ne change jamais une fois qu'il a été créé; vous pouvez le considérer comme l'adresse de l'objet en mémoire.

Vous pouvez contrôler le comportement de comparaison des valeurs d'objet en définissant une __cmp__méthode ou une méthode de comparaison riche comme __eq__.

Dave Webb
la source
3

En bref, isvérifie si deux références pointent vers le même objet ou non. ==vérifie si deux objets ont la même valeur ou non.

a=[1,2,3]
b=a        #a and b point to the same object
c=list(a)  #c points to different object 

if a==b:
    print('#')   #output:#
if a is b:
    print('##')  #output:## 
if a==c:
    print('###') #output:## 
if a is c:
    print('####') #no output as c and a point to different object 
suvojit_007
la source
2

Comme l'a dit John Feminella, la plupart du temps, vous utiliserez == et! = Car votre objectif est de comparer les valeurs. Je voudrais simplement classer ce que vous feriez le reste du temps:

Il existe une et une seule instance de NoneType, c'est-à-dire que None est un singleton. Par conséquent foo == Noneet foo is Nonesignifient la même chose. Cependant, le istest est plus rapide et la convention Pythonic est à utiliser foo is None.

Si vous effectuez une introspection ou un nettoyage avec la collecte des ordures ou vérifiez si votre gadget d'internement de chaînes personnalisé fonctionne ou similaire, alors vous avez probablement un cas d'utilisation pour foois bar.

Vrai et Faux sont également (maintenant) des singletons, mais il n'y a pas de cas d' foo == Trueutilisation ni de cas d'utilisation pour foo is True.

John Machin
la source
2

La plupart d'entre eux ont déjà répondu au point. Tout comme une note supplémentaire (basée sur ma compréhension et mes expériences mais pas à partir d'une source documentée), la déclaration

== si les objets référencés par les variables sont égaux

des réponses ci-dessus doivent être lues comme

== si les objets référencés par les variables sont égaux et les objets appartenant au même type / classe

. Je suis arrivé à cette conclusion sur la base du test ci-dessous:

list1 = [1,2,3,4]
tuple1 = (1,2,3,4)

print(list1)
print(tuple1)
print(id(list1))
print(id(tuple1))

print(list1 == tuple1)
print(list1 is tuple1)

Ici, le contenu de la liste et du tuple sont identiques mais le type / classe est différent.

Sandeep
la source
2

Différence Python entre is et equals (==)

L'opérateur is peut sembler identique à l'opérateur d'égalité mais ils ne sont pas identiques.

Le est vérifie si les deux variables pointent vers le même objet tandis que le signe == vérifie si les valeurs des deux variables sont les mêmes.

Donc, si l'opérateur is renvoie True, l'égalité est définitivement True, mais l'inverse peut ou non être True.

Voici un exemple pour démontrer la similitude et la différence.

>>> a = b = [1,2,3]
>>> c = [1,2,3]
>>> a == b
True
>>> a == c
True
>>> a is b
True
>>> a is c
False
>>> a = [1,2,3]
>>> b = [1,2]
>>> a == b
False
>>> a is b
False
>>> del a[2]
>>> a == b
True
>>> a is b
False
Tip: Avoid using is operator for immutable types such as strings and numbers, the result is unpredictable.
Projesh Bhoumik
la source
1
Veuillez utiliser uniquement des guillemets pour le texte que vous avez cité d'une autre source, auquel cas vous devez inclure l'attribution (voir stackoverflow.com/help/referencing ). S'il s'agit de votre propre texte, veuillez supprimer les guillemets.
Martijn Pieters
0

Comme les autres personnes de ce message répondent en détail à la question, je soulignerais principalement la comparaison entre iset == pour les chaînes qui peuvent donner des résultats différents et j'exhorte les programmeurs à les utiliser avec soin.

Pour la comparaison de chaînes, assurez-vous d'utiliser ==au lieu de is:

str = 'hello'
if (str is 'hello'):
    print ('str is hello')
if (str == 'hello'):
    print ('str == hello')

En dehors:

str is hello
str == hello

Mais dans l'exemple ci-dessous ==et isobtiendra des résultats différents:

str = 'hello sam'
    if (str is 'hello sam'):
        print ('str is hello sam')
    if (str == 'hello sam'):
        print ('str == hello sam')

En dehors:

str == hello sam

Conclusion:

Utilisez issoigneusement pour comparer les chaînes

imanzabet
la source
pourquoi "est" "fonctionne comme ça pour les chaînes avec des espaces?
Akash Gupta
Selon les réponses précédentes: il semble que python effectue la mise en cache sur de petits nombres entiers et chaînes, ce qui signifie qu'il utilise la même référence d'objet pour les occurrences de chaîne `` hello '' dans cet instantané de code, alors qu'il n'a pas effectué la mise en cache pour `` hello sam '' telle qu'elle est relativement plus grand que 'bonjour' (c'est-à-dire qu'il gère différentes références de la chaîne 'bonjour sam', et c'est pourquoi l'opérateur 'est' retourne faux dans l'exemple suivant) Veuillez me corriger si je me trompe
Rida Shamasneh