Pourquoi la comparaison de chaînes en utilisant '==' ou 'is' produit-elle parfois un résultat différent?


1147

J'ai un programme Python où deux variables sont définies sur la valeur 'public'. Dans une expression conditionnelle, j'ai la comparaison var1 is var2qui échoue, mais si je la change, var1 == var2elle revient True.

Maintenant, si j'ouvre mon interpréteur Python et que je fais la même comparaison "is", cela réussit.

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

Qu'est-ce que j'oublie ici?



3
Ce problème se produit également lorsque vous lisez une entrée de la console par exemple: input = raw_input("Decide (y/n): "). Dans ce cas, une entrée "y" et if input == 'y':renverra "True" tandis que if input is 'y':renverra False.
Semjon Mössinger

4
Ce blog fournit une explication bien plus complète que toute réponse guilload.com/python-string-interning
Chris_Rands

1
Comme @ chris-rico le mentionne, j'ai une excellente explication ici stackoverflow.com/q/15541404/1695680
ThorSummoner

3
Duplication possible de Y a
Taknok

Réponses:


1533

isest un test d'identité, ==est un test d'égalité. ce qui se passe dans votre code serait émulé dans l'interpréteur comme ceci:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

donc pas étonnant qu'ils ne soient pas pareils, non?

En d'autres termes: isleid(a) == id(b)


17
ahh comme eq? vs égal? dans le schéma, je l'ai.
jottos

47
Ou ==vs .equals()en Java. La meilleure partie est que le Python ==n'est pas analogue au Java ==.
MatrixFrog

11
@ Крайст: il n'y a qu'une seule Nonevaleur. Il a donc toujours le même identifiant.
SilentGhost

18
Cela ne concerne pas l'exemple "is -> True" de l'OP.
user2864740

6
@AlexanderSupertramp, en raison de l' internement de chaînes .
Chris Rico

570

Les autres réponses ici sont correctes: isest utilisée pour la comparaison d' identité , tandis que ==est utilisée pour la comparaison d' égalité . Étant donné que vous vous souciez de l'égalité (les deux chaînes doivent contenir les mêmes caractères), dans ce cas, l' isopérateur est tout simplement faux et vous devez utiliser à la ==place.

La raison pour laquelle cela isfonctionne de manière interactive est que (la plupart) des littéraux de chaîne sont internés par défaut. De Wikipédia:

Les chaînes internes accélèrent les comparaisons de chaînes, qui sont parfois un goulot d'étranglement dans les performances des applications (comme les compilateurs et les exécutions de langage de programmation dynamique) qui reposent fortement sur des tables de hachage avec des clés de chaîne. Sans internement, vérifier que deux chaînes différentes sont égales implique d'examiner chaque caractère des deux chaînes. C'est lent pour plusieurs raisons: c'est intrinsèquement O (n) dans la longueur des cordes; cela nécessite généralement des lectures dans plusieurs régions de la mémoire, ce qui prend du temps; et les lectures remplissent le cache du processeur, ce qui signifie qu'il y a moins de cache disponible pour d'autres besoins. Avec les chaînes internes, un simple test d'identité d'objet suffit après l'opération interne d'origine; ceci est généralement implémenté comme un test d'égalité de pointeur,

Ainsi, lorsque vous avez deux littéraux de chaîne (mots qui sont littéralement tapés dans le code source de votre programme, entourés de guillemets) dans votre programme qui ont la même valeur, le compilateur Python internera automatiquement les chaînes, les rendant toutes les deux stockées en même temps emplacement de la mémoire. (Notez que cela ne se produit pas toujours , et les règles pour quand cela se produit sont assez compliquées, alors ne vous fiez pas à ce comportement dans le code de production!)

Étant donné que dans votre session interactive, les deux chaînes sont réellement stockées dans le même emplacement de mémoire, elles ont la même identité , de sorte que l' isopérateur fonctionne comme prévu. Mais si vous construisez une chaîne par une autre méthode (même si cette chaîne contient exactement les mêmes caractères), alors la chaîne peut être égale , mais ce n'est pas la même chaîne - c'est-à-dire qu'elle a une identité différente , car elle est stocké dans un autre endroit en mémoire.


6
Où peut-on en savoir plus sur les règles alambiquées lorsque les chaînes sont internées?
Noctis Skytower

89
+1 pour une explication approfondie. Je ne sais pas comment l'autre réponse a reçu autant de votes positifs sans expliquer ce qui s'est réellement passé.
That1Guy

4
c'est exactement ce à quoi j'ai pensé en lisant la question. La réponse acceptée est courte mais contient le fait, mais cette réponse explique bien mieux les choses. Agréable!
Sнаđошƒаӽ

3
@NoctisSkytower a fait la même recherche sur Google
xtreak

5
@ naught101: Non, la règle est de choisir entre ==et en isfonction du type de vérification que vous souhaitez. Si vous vous souciez de l' égalité des chaînes (c'est-à-dire du même contenu), vous devez toujours utiliser ==. Si vous vous souciez de savoir si deux noms Python font référence à la même instance d'objet, vous devez utiliser is. Vous pourriez avoir besoin issi vous écrivez du code qui gère de nombreuses valeurs différentes sans se soucier de leur contenu, ou bien si vous savez qu'il n'y a qu'un seul élément et que vous souhaitez ignorer d'autres objets prétendant être cette chose. Si vous n'êtes pas sûr, choisissez toujours ==.
Daniel Pryden du

108

Le ismot-clé est un test d'identité d'objet alors qu'il ==s'agit d'une comparaison de valeurs.

Si vous utilisez is, le résultat sera vrai si et seulement si l'objet est le même objet. Cependant, ==cela sera vrai chaque fois que les valeurs de l'objet seront les mêmes.


57

Une dernière chose à noter, vous pouvez utiliser la sys.internfonction pour vous assurer d'obtenir une référence à la même chaîne:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

Comme indiqué ci-dessus, vous ne devez pas utiliser ispour déterminer l'égalité des chaînes. Mais cela peut être utile de savoir si vous avez une sorte d'exigence étrange à utiliser is.

Notez que la internfonction était auparavant un module intégré sur Python 2 mais a été déplacée vers le sysmodule en Python 3.


43

isest un test d'identité, ==est un test d'égalité. Cela signifie que isc'est un moyen de vérifier si deux choses sont les mêmes , ou tout simplement équivalentes.

Disons que vous avez un personobjet simple . S'il s'appelle «Jack» et a «23» ans, c'est l'équivalent d'un autre Jack âgé de 23 ans, mais ce n'est pas la même personne.

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

Ils ont le même âge, mais ce n'est pas le même exemple de personne. Une chaîne peut être équivalente à une autre, mais ce n'est pas le même objet.


Si vous changez de set jack1.age = 99, cela ne changera pas jack2.age. C'est parce que ce sont deux instances différentes, donc jack1 is not jack2. Cependant, ils peuvent s'égaliser jack1 == jack2si leur nom et leur âge sont identiques. Cela devient plus compliqué pour les chaînes, car les chaînes sont immuables en Python, et Python réutilise souvent la même instance. J'aime cette explication car elle utilise les cas simples (un objet normal) plutôt que les cas spéciaux (chaînes).
Flimm

37

Ceci est une note secondaire, mais en python idiomatique, vous verrez souvent des choses comme:

if x is None: 
    # some clauses

Ceci est sûr, car il est garanti qu'il y aura une instance de l'objet nul (c'est-à-dire aucune) .


1
Est-ce la même chose pour le vrai et le faux? Une seule instance correspondra donc?
HandyManDan

1
@HandyManDan Oui, ce sont des singletons en python 2 et 3.
kamillitw

@kamillitw mais en Python 2, vous pouvez réaffecter False et True.
Martijn Pieters

28

Si vous n'êtes pas sûr de ce que vous faites, utilisez le '=='. Si vous en avez un peu plus, vous pouvez utiliser «est» pour des objets connus comme «Aucun».

Sinon, vous finirez par vous demander pourquoi les choses ne fonctionnent pas et pourquoi cela se produit:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

Je ne sais même pas si certaines choses sont garanties de rester les mêmes entre les différentes versions / implémentations de python.


1
Exemple intéressant montrant comment la réaffectation des entrées déclenche cette condition. Pourquoi cela a-t-il échoué? Est-ce dû à l'internement ou à autre chose?
Paul

Il semble que la raison pour laquelle il retourne false peut être due à la mise en œuvre de l'interpréteur: stackoverflow.com/questions/132988/…
Paul


@ArchitJain Oui, ces liens l'expliquent assez bien. Lorsque vous les lirez, vous saurez sur quels numéros vous pouvez utiliser "est". J'aimerais juste qu'ils expliquent pourquoi ce n'est toujours pas une bonne idée de le faire :) Vous savez que cela ne fait pas une bonne idée de supposer que tout le monde le fait aussi (ou que la plage de numéros internalisée ne changera jamais)
Mattias Nilsson

20

D'après mon expérience limitée avec python, isest utilisé pour comparer deux objets pour voir s'ils sont le même objet par opposition à deux objets différents avec la même valeur. ==est utilisé pour déterminer si les valeurs sont identiques.

Voici un bon exemple:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1est une chaîne unicode et s2est une chaîne normale. Ils ne sont pas du même type, mais ont la même valeur.


17

Je pense que cela a à voir avec le fait que, lorsque la comparaison «est» est évaluée à faux, deux objets distincts sont utilisés. S'il vaut true, cela signifie en interne qu'il utilise le même objet exact et n'en crée pas un nouveau, peut-être parce que vous les avez créés dans une fraction de 2 secondes environ et parce qu'il n'y a pas de grand écart de temps entre les deux, il est optimisé et utilise le même objet.

C'est pourquoi vous devez utiliser l'opérateur d'égalité ==, non is, pour comparer la valeur d'un objet chaîne.

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

Dans cet exemple, j'ai créé s2, qui était un objet chaîne différent précédemment égal à "un" mais ce n'est pas le même objet que s, car l'interpréteur n'a pas utilisé le même objet que je ne l'ai pas affecté initialement à "un", si je l'avais fait, cela aurait fait d'eux le même objet.


3
.replace()Cependant, utiliser comme exemple dans ce contexte n'est probablement pas le meilleur, car sa sémantique peut être déroutante. s2 = s2.replace()sera toujours créer un nouvel objet chaîne, affecter le nouvel objet chaîne à s2, puis disposer de l'objet chaîne qui s2sert à pointer. Donc, même si vous le faisiez, s = s.replace('one', 'one')vous obtiendrez toujours un nouvel objet chaîne.
Daniel Pryden

13

Je crois que cela est connu sous le nom de chaînes "internées". Python fait cela, Java aussi, ainsi que C et C ++ lors de la compilation en modes optimisés.

Si vous utilisez deux chaînes identiques, au lieu de gaspiller de la mémoire en créant deux objets chaîne, toutes les chaînes internes avec le même contenu pointent vers la même mémoire.

Il en résulte que l'opérateur "is" Python renvoie True car deux chaînes avec le même contenu pointent vers le même objet chaîne. Cela se produira également en Java et en C.

Cependant, cela n'est utile que pour économiser de la mémoire. Vous ne pouvez pas vous y fier pour tester l'égalité des chaînes, car les divers interprètes, compilateurs et moteurs JIT ne peuvent pas toujours le faire.


12

Je réponds à la question même si la question est trop ancienne car aucune des réponses ci-dessus ne cite la référence de la langue

En fait, l'opérateur is vérifie l'identité et l'opérateur == vérifie l'égalité,

De la référence du langage:

Les types affectent presque tous les aspects du comportement des objets. Même l'importance de l'identité d'objet est affectée dans un certain sens: pour les types immuables, les opérations qui calculent de nouvelles valeurs peuvent en fait renvoyer une référence à tout objet existant avec le même type et la même valeur, tandis que pour les objets mutables, cela n'est pas autorisé . Par exemple, après a = 1; b = 1, a et b peuvent ou non faire référence au même objet avec la valeur un, selon l'implémentation, mais après c = []; d = [], c et d sont garantis pour faire référence à deux listes vides différentes, uniques et nouvellement créées. (Notez que c = d = [] attribue le même objet à la fois à c et à d.)

donc à partir de la déclaration ci-dessus, nous pouvons déduire que les chaînes qui sont un type immuable peuvent échouer lorsqu'elles sont vérifiées avec "est" et peuvent réussir si elles sont vérifiées avec "est"

Il en va de même pour les tuple int, qui sont également des types immuables


8

L' ==équivalence de la valeur de test de l'opérateur. L' isopérateur teste l'identité de l'objet, Python teste si les deux sont vraiment le même objet (c'est-à-dire vivent à la même adresse en mémoire).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

Dans cet exemple, Python n'a créé qu'un seul objet chaîne, et les deux, aet s'y bréfère. La raison en est que Python met en cache et réutilise en interne certaines chaînes comme optimisation, il n'y a vraiment qu'une chaîne «banane» en mémoire, partagée par a et b; Pour déclencher le comportement normal, vous devez utiliser des chaînes plus longues:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

Lorsque vous créez deux listes, vous obtenez deux objets:

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

Dans ce cas, nous dirions que les deux listes sont équivalentes, car elles ont les mêmes éléments, mais pas identiques, car elles ne sont pas le même objet. Si deux objets sont identiques, ils sont également équivalents, mais s'ils sont équivalents, ils ne sont pas nécessairement identiques.

Si afait référence à un objet et que vous l'assignez b = a, les deux variables font référence au même objet:

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

7

iscomparera l'emplacement de la mémoire. Il est utilisé pour la comparaison au niveau de l'objet.

==comparera les variables du programme. Il est utilisé pour vérifier à un niveau de valeur.

is vérifie l'équivalence au niveau de l'adresse

== vérifie l'équivalence du niveau de valeur


3

isest un test d'identité, ==est un test d'égalité (voir la documentation Python ).

Dans la plupart des cas, si a is b, alors a == b. Mais il y a des exceptions, par exemple:

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

Ainsi, vous ne pouvez utiliser que ispour les tests d'identité, jamais les tests d'égalité.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.