web-dev-qa-db-fra.com

Pourquoi comparer des chaînes en utilisant '==' ou 'is' produit-il parfois un résultat différent?

J'ai un programme Python dans lequel deux variables sont définies sur la valeur 'public'. Dans une expression conditionnelle, j'ai la comparaison var1 is var2 qui échoue, mais si je la change en var1 == var2, elle retourne 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?

1052
jottos

is est le test d'identité, == est le test d'égalité. ce qui se passe dans votre code serait imité dans l'interprète comme ceci:

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

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

En d'autres termes: is est la id(a) == id(b)

1427
SilentGhost

Les autres réponses sont correctes: is est utilisé pour la comparaison identité , tandis que == est utilisé pour égalité comparaison. Puisque ce qui vous importe, c’est l’égalité (les deux chaînes doivent contenir les mêmes caractères), dans ce cas, l’opérateur is est tout simplement faux et vous devriez plutôt utiliser ==.

La raison pour laquelle is fonctionne de manière interactive est que (la plupart) les littéraux de chaîne sont internés par défaut. De Wikipedia:

Les chaînes internées accélèrent les comparaisons de chaînes, qui constituent parfois un goulot d'étranglement dans les applications (telles que les compilateurs et les environnements d'exécution de langage de programmation dynamiques) qui reposent fortement sur les tables de hachage avec des clés de chaîne. Sans interner, vérifier que deux chaînes différentes sont égales implique d'examiner chaque caractère des deux chaînes. Ceci est lent pour plusieurs raisons: il est intrinsèquement O(n) dans la longueur des chaînes; cela nécessite généralement des lectures de 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 internées, un simple test d'identité d'objet suffit après l'opération interne initiale; Ceci est généralement implémenté comme un test d'égalité de pointeur, normalement une simple instruction d'ordinateur sans référence à la mémoire.

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

É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'opérateur is fonctionne comme attendu. 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 égal , mais ce n'est pas la même chaîne - c'est-à-dire qu'il a une autre identity , car il est stocké dans un emplacement différent en mémoire.

522
Daniel Pryden

Le mot clé is est un test d'identité d'identité alors que == est une comparaison de valeur.

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

100
Thomas Owens

Une dernière chose à noter, vous pouvez utiliser la fonction intern pour vous assurer que vous obtenez une référence à la même chaîne:

>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

Comme indiqué ci-dessus, vous ne devez probablement pas déterminer l’égalité des chaînes. Mais cela peut être utile de savoir si vous avez une sorte d’obligation bizarre d’utiliser is.

Notez que la fonction intern est passée de la fonction intégrée au module sys pour Python 3.

55
Jason Baker

is est le test d'identité, == est le test d'égalité. Cela signifie que is est un moyen de vérifier si deux choses sont identiques ou simplement équivalentes.

Supposons que vous ayez un simple objet person. Si elle porte le nom "Jack" et a 23 ans, cela équivaut à 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 ne sont pas les mêmes personnes. Une chaîne peut être équivalente à une autre, mais ce n'est pas le même objet.

40
TankorSmash

Ceci est une note de côté, mais en python idiomatique, vous verrez souvent des choses comme:

if x is None: 
    # some clauses

Ceci est sans danger, car il est garanti qu’il n’ya qu’une seule instance de l’objet Null (aucun) .

35
Gregg Lind

Si vous n'êtes pas sûr de ce que vous faites, utilisez le '=='. Si vous avez un peu plus de connaissances à ce sujet, vous pouvez utiliser "is" pour des objets connus tels que "Aucun".

Sinon, vous vous demanderez 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 suis même pas sûr qu'il soit garanti que certaines choses resteront identiques entre différentes versions/implémentations de python.

26
Mattias Nilsson

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

Voici un bon exemple:

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

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

19
Jack M.

Je pense que cela a à voir avec le fait que, lorsque la comparaison "est" est évaluée comme fausse, deux objets distincts sont utilisés. Si true est évalué, cela signifie en interne qu’il utilise le même objet et ne crée pas de nouvel objet, peut-être parce que vous les avez créés en une fraction de seconde environ et qu’il n’ya pas un intervalle de temps important entre l’optimisation et utilise le même objet.

C'est pourquoi vous devriez utiliser l'opérateur d'égalité == et non pas 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 de 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 celui que je n'avais pas initialement affecté. à 'un', si j'avais eu ça leur aurait fait le même objet.

16
meder omuraliev

Je crois que cela s'appelle des chaînes "internées". Python le fait, ainsi que Java et C et C++ lors de la compilation dans des 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 internées avec le même contenu sont dirigées vers la même mémoire.

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

Ceci 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 différents interpréteurs, compilateurs et moteurs JIT ne peuvent pas toujours le faire.

12
Zan Lynx

Je réponds à la question même si la question est trop ancienne car aucune réponse ne cite la référence du langage ci-dessus

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

De 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 une certaine mesure: 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, alors 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, en fonction de l'implémentation, mais après c = []; d = [], c et d sont assurés de faire référence à deux listes vides différentes, uniques et nouvellement créées. (Notez que c = d = [] assigne le même objet à 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 si elles sont cochées avec "est" et peuvent être cochées si elles sont cochées avec "est"

Il en va de même pour int, Tuple, qui sont aussi des types immuables

10
Ram

L'équivalence de la valeur de test de l'opérateur ==. L'opérateur is teste l'identité de l'objet, Python vérifie si les deux sont vraiment le même objet (c'est-à-dire qu'ils résident à 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 a et b y font référence. La raison en est que Python met en cache en interne et réutilise certaines chaînes en tant qu'optimisation. Il n'y a en réalité 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 comportent les mêmes éléments, mais ne sont pas identiques, car elles ne sont pas le même objet. Si deux objets sont identiques, ils sont également équivalents, mais s'ils ne sont pas équivalents, ils ne sont pas nécessairement identiques.

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

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
7
X. Wang

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

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

is vérifie l'équivalence du niveau d'adresse

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

2
johnashu

is est le test d'identité, == est le test d'égalité (voir 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 is pour les tests d'identité, jamais les tests d'égalité.

2
Ryan