Comprendre l'opérateur «est» de Python

110

L' isopérateur ne correspond pas aux valeurs des variables, mais aux instances elles-mêmes.

Qu'est-ce que cela signifie vraiment?

J'ai déclaré deux variables nommées xet yaffectant les mêmes valeurs dans les deux variables, mais cela renvoie false lorsque j'utilise l' isopérateur.

J'ai besoin d'une clarification. Voici mon code.

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!
aniskhan001
la source
Question connexe stackoverflow.com/questions/38189660/…
Kasravnd

Réponses:

181

Vous avez mal compris ce que isteste l' opérateur. Il teste si deux variables pointent sur le même objet , pas si deux variables ont la même valeur.

À partir de la documentation pour l' isopérateur :

Les opérateurs iset is nottest pour l'identité d'objet: x is yest vrai si et seulement si xet ysont le même objet.

Utilisez ==plutôt l' opérateur:

print(x == y)

Cela imprime True. xet ysont deux listes distinctes :

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

Si vous utilisez la id()fonction, vous verrez cela xet yaurez des identifiants différents:

>>> id(x)
4401064560
>>> id(y)
4401098192

mais si vous deviez attribuer yà xalors les deux pointent vers le même objet:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

et ismontre que les deux sont le même objet, il retourne True.

N'oubliez pas qu'en Python, les noms ne sont que des étiquettes référençant des valeurs ; vous pouvez faire pointer plusieurs noms vers le même objet. isvous indique si deux noms pointent vers un seul et même objet. ==vous indique si deux noms font référence à des objets qui ont la même valeur.

Martijn Pieters
la source
13
Donc, A is Bc'est le même que id(A) == id(B).
imallett
2
@imallett: c'est un proxy pour le même test, à condition que vous ne stockiez pas id(A)dans une variable et que vous variable == id(B)vous attendiez plus tard à continuer de fonctionner; si elle a Aété supprimée entre-temps, Ble même emplacement de mémoire aurait pu être attribué.
Martijn Pieters
1
Impossible de formater les commentaires. Mais il y a une chose intéressante. :) >>> x = 5 \n>>> y = 5 \n>>> x est y \nVrai \n>>> x == y \nVrai \n>>>\n
Haranadh
5
Les petits entiers sont internés dans CPython car ils sont utilisés si souvent. C'est une optimisation. x = 5; y = 5; x est y => Vrai car id (x) == id (y). C'est le même objet entier qui est réutilisé. Fonctionne en Python car les entiers sont immuables. Si vous faites x = 1,0; y = 1.0 ou x = 9999; y = 9999, ce ne sera pas la même identité, car les flottants et les entiers plus grands ne sont pas internés.
Magnus Lyckå
1
@ MagnusLyckå il existe d'autres optimisations qui peuvent provoquer la mise en cache d'objets immuables. Par exemple, si vous exécutez votre exemple dans une nouvelle fonction ou avec un point-virgule de séparation dans l'interpréteur interactif, vous constaterez qu'ils ont également le même identifiant.
Martijn Pieters
60

Un autre doublon demandait pourquoi deux chaînes égales ne sont généralement pas identiques, ce qui n'est pas vraiment répondu ici:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

Alors, pourquoi ne sont-ils pas la même chaîne? Surtout compte tenu de ceci:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

Retardons un peu la deuxième partie. Comment le premier pourrait-il être vrai?

L'interpréteur devrait avoir une "table interne", une table mappant des valeurs de chaîne à des objets de chaîne, donc chaque fois que vous essayez de créer une nouvelle chaîne avec le contenu 'abc', vous récupérez le même objet. Wikipedia a une discussion plus détaillée sur le fonctionnement du stage.

Et Python a une table interne de chaînes; vous pouvez manuellement interner des chaînes avec la sys.internméthode.

En fait, Python est autorisé à interner automatiquement tous les types immuables, mais pas obligé de le faire. Différentes implémentations intègreront différentes valeurs.

CPython (l'implémentation que vous utilisez si vous ne savez pas quelle implémentation vous utilisez) auto-interne de petits entiers et certains singletons spéciaux comme False, mais pas des chaînes (ou de grands entiers, ou de petits tuples, ou autre chose). Vous pouvez voir cela assez facilement:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

OK, mais pourquoi étaient zet widentiques?

Ce n'est pas l'interpréteur qui effectue automatiquement l'internation, ce sont les valeurs de repli du compilateur.

Si la même chaîne de compilation apparaît deux fois dans le même module (ce que cela signifie exactement est difficile à définir, ce n'est pas la même chose comme un littéral de chaîne, parce que r'abc', 'abc'et 'a' 'b' 'c'sont toutes différentes littéraux , mais la même chaîne, mais facile à comprendre intuitivement), le compilateur ne créera qu'une seule instance de la chaîne, avec deux références.

En fait, le compilateur peut aller encore plus loin: 'ab' + 'c'peut être converti en 'abc'par l'optimiseur, auquel cas il peut être plié avec une 'abc'constante dans le même module.

Encore une fois, c'est quelque chose que Python est autorisé mais pas obligé de faire. Mais dans ce cas, CPython plie toujours de petites chaînes (et aussi, par exemple, de petits tuples). (Bien que le compilateur instruction par instruction de l'interpréteur interactif n'exécute pas la même optimisation que le compilateur module à la fois, vous ne verrez donc pas exactement les mêmes résultats de manière interactive.)


Alors, que devez-vous faire à ce sujet en tant que programmeur?

Eh bien… rien. Vous n'avez presque jamais aucune raison de vous soucier si deux valeurs immuables sont identiques. Si vous voulez savoir quand vous pouvez utiliser à la a is bplace de a == b, vous posez la mauvaise question. Il suffit de toujours utiliser a == bsauf dans deux cas:

  • Pour des comparaisons plus lisibles avec les valeurs de singleton comme x is None.
  • Pour les valeurs mutables, lorsque vous avez besoin de savoir si la mutation xaffectera le y.
Abarnert
la source
1
Excellente explication, surtout vos conseils à la fin.
DavidG
Merci pour cette explication détaillée. Est-ce que quelqu'un sait: si wet zsont identiques à cause des valeurs de repli du compilateur, pourquoi cela fonctionne-t-il également dans le REPL, même en utilisant id()pour vérifier les références? Utilisation du REPL sur Python 3.7
Chi-chi
8

isne renvoie true que s'il s'agit en fait du même objet. S'ils étaient identiques, un changement dans l'un apparaîtrait également dans l'autre. Voici un exemple de la différence.

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]
Mark Ransom
la source
8

Invité par une question en double , cette analogie pourrait fonctionner:

# - Darling, I want some pudding!
# - There is some in the fridge.

pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True

# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.

pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False
Amadan
la source
3
Cela pourrait être juste un goût personnel (sans jeu de mots) mais j'ai trouvé cette analogie plus déroutante qu'utile et m'a donné envie de manger du pudding quand je n'en ai pas dans mon réfrigérateur: (Je pense que la réponse de Mark Ransom, bien que plus ennuyeuse, est probablement plus instructif
Tom Close
1
@TomClose: Il y a beaucoup de bonnes réponses à cette question, assez pour qu'il y ait de la place pour la légèreté. Aussi, je veux du pudding aussi.
Amadan
5

iset is notsont les deux opérateurs d'identité en Python. isL'opérateur ne compare pas les valeurs des variables, mais compare les identités des variables. Considère ceci:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

L'exemple ci-dessus vous montre que l'identité (peut également être l'adresse mémoire en Cpython) est différente pour les deux aet b(même si leurs valeurs sont identiques). C'est pourquoi lorsque vous dites a is bqu'il renvoie false en raison de la non-concordance dans les identités des deux opérandes. Cependant, lorsque vous dites a == b, il renvoie true car l' ==opération vérifie uniquement si les deux opérandes ont la même valeur qui leur est attribuée.

Exemple intéressant (pour le grade supplémentaire):

>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>

Dans l'exemple ci-dessus, même si aet bsont deux variables différentes, a is brenvoyées True. C'est parce que le type de aest intqui est un objet immuable. Donc, python (je suppose pour économiser de la mémoire) a alloué le même objet à bsa création avec la même valeur. Donc, dans ce cas, les identités des variables correspondaient et a is bse sont avérées être True.

Cela s'appliquera à tous les objets immuables:

>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>

J'espère que cela pourra aider.

gixxer
la source
c'est un bel exemple. merci pour des informations détaillées.
Haranadh
Mais essayez a = 123456789 b = 123456789
user2183078
Tout ce qui est inférieur -5ou supérieur 256à Python sera faux. Python met en cache les nombres dans la plage [-5, 256].
smart
Tous les objets immuables ne seront pas partagés comme vous le montrez, c'est une optimisation appliquée par le runtime Python pour certains objets mais pas pour d'autres. Le processus de partage de petits entiers est bien documenté, mais je ne pense pas que ce soit pour les stages de chaînes .
Mark Ransom
4

x is yest identique à la id(x) == id(y)comparaison de l'identité des objets.

Comme @ tomasz-kurgan l'a souligné dans le commentaire ci-dessous, l' isopérateur se comporte de manière inhabituelle avec certains objets.

Par exemple

>>> class A(object):
...   def foo(self):
...     pass
... 
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True

Réf;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24

Nizam Mohamed
la source
Non, ce n'est pas le cas. Cela peut se comporter de manière similaire dans la plupart des cas, mais ce n'est pas toujours vrai. Voir ceci - tout en bas de la page, puce 6:> (...), vous pouvez remarquer un comportement apparemment inhabituel dans certaines utilisations de l' opérateur is , comme celles impliquant des comparaisons entre des méthodes d'instance ou des constantes Et l'exemple de travail minimal : `class A (object): def foo (self): passer a = A () print a.foo est a.foo print id (a.foo) == id (a.foo)`
Tomasz Kurgan
3

Comme vous pouvez le vérifier ici à un petit nombre entier. Les nombres supérieurs à 257 ne sont pas de petits nombres, ils sont donc calculés comme un objet différent.

Il est préférable d'utiliser à la ==place dans ce cas.

Plus d'informations ici: http://docs.python.org/2/c-api/int.html

CS Gamer
la source
2

X pointe vers un tableau, Y pointe vers un autre tableau. Ces tableaux sont identiques, mais l' isopérateur examinera ces pointeurs, qui ne sont pas identiques.

Neko
la source
5
Python n'a pas de pointeurs. Vous devez resserrer votre terminologie.
David Heffernan
3
Il le fait en interne, tout comme Java et tant d'autres langages. En fait, la isfonctionnalité de l' opérateur le montre.
Neko
5
Les détails de mise en œuvre ne sont pas ce qui compte. La documentation utilise la terminologie «identité d'objet». Vous devriez donc. "L'opérateur est et n'est pas un test d'identité d'objet: x est y est vrai si et seulement si x et y sont le même objet. X n'est pas y donne la valeur de vérité inverse."
David Heffernan
1
@Neko: CPython utilise en interne des pointeurs. Mais évidemment, Jython (implémenté en Java) et PyPy (implémenté dans un sous-ensemble de Python) n'utilisent pas de pointeurs. Dans PyPy, certains objets n'auront même pas de fichier à idmoins que vous ne le demandiez.
abarnert le
1

Il compare l'identité de l'objet, c'est-à-dire si les variables font référence au même objet en mémoire. C'est comme ==dans Java ou C (en comparant les pointeurs).

mipadi
la source
1

Un exemple simple avec des fruits

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )

Production:

True
False
False

Si tu essayes

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )

La sortie est différente:

True
True
True

C'est parce que l'opérateur == compare uniquement le contenu de la variable. Pour comparer les identités de 2 variables, utilisez l' opérateur is

Pour imprimer le numéro d'identification:

print ( id( variable ) )
RéprouvésUn
la source
-3

L' isopérateur n'est rien d'autre qu'une version anglaise de ==. Parce que les ID des deux listes sont différents, la réponse est fausse. Tu peux essayer:

a=[1,2,3]
b=a
print(b is a )#True

* Parce que les identifiants des deux listes seraient les mêmes

Aadit
la source
isn'est pas `` une version anglaise de ==''
David Buck