Warum führt der Vergleich von Zeichenfolgen mit '==' oder 'is' manchmal zu einem anderen Ergebnis?

1146

Ich habe ein Python-Programm, in dem zwei Variablen auf den Wert gesetzt sind 'public'. In einem bedingten Ausdruck habe ich den Vergleich, var1 is var2der fehlschlägt, aber wenn ich ihn ändere, wird var1 == var2er zurückgegeben True.

Wenn ich jetzt meinen Python-Interpreter öffne und den gleichen "Ist" -Vergleich mache, ist dies erfolgreich.

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

Was fehlt mir hier?

Jottos
quelle
8
siehe: stackoverflow.com/questions/1392433/…
Nick Dandoulakis
3
Dieses Problem tritt auch auf, wenn Sie eine Konsoleneingabe lesen, z input = raw_input("Decide (y/n): "). B.: . In diesem Fall gibt eine Eingabe von "y" und if input == 'y':"True" zurück, während " if input is 'y':False " zurückgegeben wird.
Semjon Mössinger
4
Dieser Blog bietet eine weitaus vollständigere Erklärung als jede Antwort guilload.com/python-string-interning
Chris_Rands
1
Wie @ chris-rico erwähnt, habe ich hier eine gute
ThorSummoner

Antworten:

1533

is ist Identitätsprüfung, == ist Gleichheitstest. Was in Ihrem Code passiert, wird im Interpreter folgendermaßen emuliert:

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

Kein Wunder also, dass sie nicht gleich sind, oder?

Mit anderen Worten: isist dieid(a) == id(b)

SilentGhost
quelle
17
ahh wie Gl. vs gleich? im Schema, habe es verstanden.
Jottos
47
Oder ==vs .equals()in Java. Das Beste daran ist, dass Python ==nicht mit Java vergleichbar ist ==.
MatrixFrog
11
@ Крайст: Es gibt nur einen einzigen NoneWert. Es hat also immer die gleiche ID.
SilentGhost
18
Dies betrifft nicht das Beispiel "is -> True" des OP.
user2864740
6
@AlexanderSupertramp, wegen String-Internierung .
Chris Rico
570

Andere Antworten hier sind richtig: iswird für den Identitätsvergleich verwendet , während ==sie für den Gleichheitsvergleich verwendet wird . Da Sie sich für Gleichheit interessieren (die beiden Zeichenfolgen sollten dieselben Zeichen enthalten), ist der isOperator in diesem Fall einfach falsch und Sie sollten ==stattdessen verwenden.

Der Grund, warum isinteraktiv funktioniert, ist, dass (die meisten) Zeichenfolgenliterale standardmäßig interniert sind . Aus Wikipedia:

Internierte Zeichenfolgen beschleunigen Zeichenfolgenvergleiche, die in Anwendungen (z. B. Compilern und Laufzeiten dynamischer Programmiersprachen), die stark von Hash-Tabellen mit Zeichenfolgenschlüsseln abhängen, manchmal einen Leistungsengpass darstellen. Um zu überprüfen, ob zwei verschiedene Zeichenfolgen gleich sind, müssen Sie ohne Internierung jedes Zeichen beider Zeichenfolgen untersuchen. Dies ist aus mehreren Gründen langsam: Es ist von Natur aus O (n) in der Länge der Zeichenfolgen; In der Regel sind Lesevorgänge aus mehreren Speicherbereichen erforderlich, die einige Zeit in Anspruch nehmen. und die Lesevorgänge füllen den Prozessor-Cache aus, was bedeutet, dass weniger Cache für andere Anforderungen verfügbar ist. Bei internierten Zeichenfolgen reicht nach der ursprünglichen internen Operation ein einfacher Objektidentitätstest aus. Dies wird normalerweise als Zeigergleichheitstest implementiert.

Wenn Sie also zwei Zeichenfolgenliterale (Wörter, die buchstäblich in Ihren Programmquellcode eingegeben werden und von Anführungszeichen umgeben sind) in Ihrem Programm haben, die denselben Wert haben, werden die Zeichenfolgen vom Python-Compiler automatisch interniert, sodass beide gleichzeitig gespeichert werden Speicherort. (Beachten Sie, dass dies nicht immer der Fall ist Fall ist und die Regeln dafür ziemlich kompliziert sind. Verlassen Sie sich daher bitte nicht auf dieses Verhalten im Produktionscode!)

Da in Ihrer interaktiven Sitzung beide Zeichenfolgen tatsächlich am selben Speicherort gespeichert sind, haben sie dieselbe Identität , sodass der isBediener wie erwartet arbeitet. Wenn Sie jedoch eine Zeichenfolge mit einer anderen Methode erstellen (auch wenn diese Zeichenfolge genau dieselben Zeichen enthält), ist die Zeichenfolge möglicherweise gleich , aber nicht dieselbe Zeichenfolge - das heißt, sie hat eine andere Identität , weil sie es ist an einem anderen Ort im Speicher gespeichert.

Daniel Pryden
quelle
6
Wo kann jemand mehr über die verschlungenen Regeln lesen, wenn Zeichenfolgen interniert werden?
Noctis Skytower
89
+1 für eine gründliche Erklärung. Ich bin mir nicht sicher, wie die andere Antwort so viele positive Stimmen erhalten hat, ohne zu erklären, was WIRKLICH passiert ist.
That1Guy
4
Genau daran habe ich gedacht, als ich die Frage gelesen habe. Die akzeptierte Antwort ist kurz, enthält jedoch die Tatsache, aber diese Antwort erklärt die Dinge weitaus besser. Nett!
Sнаđошƒаӽ
3
@NoctisSkytower Googelte das gleiche und fand diese guilload.com/python-string-interning
xtreak
5
@ naught101: Nein, die Regel ist, zwischen ==und isbasierend auf der Art der gewünschten Prüfung zu wählen . Wenn Sie sich dafür interessieren, dass die Zeichenfolgen gleich sind (dh denselben Inhalt haben), sollten Sie immer verwenden ==. Wenn Sie sich darum kümmern, ob zwei Python-Namen auf dieselbe Objektinstanz verweisen, sollten Sie verwenden is. Du brauchst vielleichtis wenn Sie Code schreiben, der viele verschiedene Werte verarbeitet, ohne sich um deren Inhalt zu kümmern, oder wenn Sie wissen, dass es nur einen von etwas gibt und Sie andere Objekte ignorieren möchten, die so tun, als wären sie das. Wenn Sie sich nicht sicher sind, wählen Sie immer ==.
Daniel Pryden
108

Das isSchlüsselwort ist ein Test für die Objektidentität und ==ein Wertevergleich.

Wenn Sie verwenden is, ist das Ergebnis genau dann wahr, wenn das Objekt dasselbe Objekt ist. Dies gilt ==jedoch immer dann, wenn die Werte des Objekts gleich sind.

Thomas Owens
quelle
57

Als letztes müssen Sie die sys.internFunktion verwenden, um sicherzustellen, dass Sie einen Verweis auf dieselbe Zeichenfolge erhalten:

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

Wie oben erwähnt, sollten Sie nicht verwenden is, um die Gleichheit von Zeichenfolgen zu bestimmen. Dies kann jedoch hilfreich sein, um zu wissen, ob Sie eine seltsame Anforderung haben is.

Beachten Sie, dass die internFunktion früher in Python 2 integriert war, aber sysin Python 3 in das Modul verschoben wurde .

Jason Baker
quelle
43

isist Identitätstest, ==ist Gleichheitstest. Dies bedeutet, dass auf diese isWeise überprüft werden kann, ob zwei Dinge gleich oder nur gleichwertig sind.

Angenommen, Sie haben ein einfaches personObjekt. Wenn es 'Jack' heißt und '23' Jahre alt ist, entspricht es einem anderen 23 Jahre alten Jack, aber es ist nicht dieselbe Person.

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

Sie sind gleich alt, aber nicht die gleiche Person. Eine Zeichenfolge entspricht möglicherweise einer anderen, ist jedoch nicht dasselbe Objekt.

TankorSmash
quelle
Wenn Sie den Satz ändern jack1.age = 99, ändert sich dies nicht jack2.age. Das liegt daran, dass es sich also um zwei verschiedene Instanzen handelt jack1 is not jack2. Sie können sich jedoch gleichen, jack1 == jack2wenn ihr Name und ihr Alter gleich sind. Für Zeichenfolgen wird es komplizierter, da Zeichenfolgen in Python unveränderlich sind und Python häufig dieselbe Instanz wiederverwendet. Ich mag diese Erklärung, weil sie eher die einfachen Fälle (ein normales Objekt) als die Sonderfälle (Zeichenfolgen) verwendet.
Flimm
37

Dies ist eine Randnotiz, aber in idiomatischer Python sehen Sie oft Dinge wie:

if x is None: 
    # some clauses

Dies ist sicher, da garantiert eine Instanz des Null-Objekts vorhanden ist (dh keine) .

Gregg Lind
quelle
1
Gilt das auch für Wahr und Falsch? Nur eine Instanz wird also übereinstimmen?
HandyManDan
1
@ HandyManDan Ja, sie sind Singletons sowohl in Python 2 als auch 3.
Kamillitw
@kamillitw, aber in Python 2 können Sie False und True neu zuweisen.
Martijn Pieters
28

Wenn Sie nicht sicher sind, was Sie tun, verwenden Sie das '=='. Wenn Sie etwas mehr darüber wissen, können Sie 'is' für bekannte Objekte wie 'None' verwenden.

Andernfalls werden Sie sich fragen, warum die Dinge nicht funktionieren und warum dies passiert:

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

Ich bin mir nicht einmal sicher, ob einige Dinge zwischen verschiedenen Python-Versionen / -Implementierungen garantiert gleich bleiben.

Mattias Nilsson
quelle
1
Interessantes Beispiel, das zeigt, wie die Neuzuweisung von Ints diese Bedingung auslöst. Warum ist das gescheitert? Liegt es am Praktikum oder an etwas anderem?
Paul
Es sieht so aus, als ob der Grund für die Rückgabe von false möglicherweise auf die Interpreter-Implementierung zurückzuführen ist: stackoverflow.com/questions/132988/…
Paul
@ArchitJain Ja, diese Links erklären es ziemlich gut. Wenn Sie sie lesen, wissen Sie, auf welchen Zahlen Sie "ist" verwenden können. Ich wünschte nur, sie würden erklären, warum es immer noch keine gute Idee ist, das zu tun :) Wenn Sie wissen, dass dies keine gute Idee ist, anzunehmen, dass dies auch alle anderen tun (oder dass sich der verinnerlichte Nummernkreis niemals ändern wird)
Mattias Nilsson
20

Aufgrund meiner begrenzten Erfahrung mit Python werden iszwei Objekte verglichen, um festzustellen, ob sie dasselbe Objekt sind, im Gegensatz zu zwei verschiedenen Objekten mit demselben Wert. ==wird verwendet, um festzustellen, ob die Werte identisch sind.

Hier ist ein gutes Beispiel:

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

s1ist eine Unicode-Zeichenfolge und s2eine normale Zeichenfolge. Sie sind nicht vom selben Typ, aber vom selben Wert.

Jack M.
quelle
17

Ich denke, es hat damit zu tun, dass, wenn der 'is'-Vergleich als falsch ausgewertet wird, zwei unterschiedliche Objekte verwendet werden. Wenn es als wahr ausgewertet wird, bedeutet dies, dass es intern genau dasselbe Objekt verwendet und kein neues erstellt, möglicherweise weil Sie es innerhalb eines Bruchteils von etwa 2 Sekunden erstellt haben und weil zwischen dem optimierten und dem optimierten Objekt keine große Zeitlücke besteht verwendet das gleiche Objekt.

Aus diesem Grunde sollten Sie den Gleichheitsoperator verwenden ==, nicht is, um den Wert eines String - Objekt zu vergleichen.

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

In diesem Beispiel habe ich s2 erstellt, ein anderes Zeichenfolgenobjekt, das zuvor gleich "Eins" war, aber nicht dasselbe Objekt wie " s, da der Interpreter nicht dasselbe Objekt verwendet hat, wie ich es ursprünglich nicht" Eins "zugewiesen habe. Wenn ich es gehabt hätte, hätte es sie zum gleichen Objekt gemacht.

meder omuraliev
quelle
3
Die Verwendung .replace()als Beispiel in diesem Zusammenhang ist jedoch wahrscheinlich nicht die beste, da die Semantik verwirrend sein kann. s2 = s2.replace()erstellt immer ein neues Zeichenfolgenobjekt, weist das neue Zeichenfolgenobjekt zu s2und entsorgt dann das Zeichenfolgenobjekt, auf das s2früher verwiesen wurde. Selbst wenn Sie dies tun s = s.replace('one', 'one')würden, würden Sie immer noch ein neues Zeichenfolgenobjekt erhalten.
Daniel Pryden
13

Ich glaube, dass dies als "internierte" Saiten bekannt ist. Python tut dies ebenso wie Java und C und C ++ beim Kompilieren in optimierten Modi.

Wenn Sie zwei identische Zeichenfolgen verwenden, anstatt Speicherplatz durch Erstellen von zwei Zeichenfolgenobjekten zu verschwenden, verweisen alle internierten Zeichenfolgen mit demselben Inhalt auf denselben Speicher.

Dies führt dazu, dass der Python-Operator "is" True zurückgibt, da zwei Zeichenfolgen mit demselben Inhalt auf dasselbe Zeichenfolgenobjekt zeigen. Dies wird auch in Java und in C geschehen.

Dies ist jedoch nur zur Speichereinsparung nützlich. Sie können sich nicht darauf verlassen, um die Zeichenfolgengleichheit zu testen, da die verschiedenen Interpreter und Compiler sowie die JIT-Engines dies nicht immer tun können.

Zan Lynx
quelle
12

Ich beantworte die Frage, obwohl die Frage zu alt ist, da keine der obigen Antworten die Sprachreferenz zitiert

Tatsächlich prüft der Operator auf Identität und der Operator auf Gleichheit.

Aus der Sprachreferenz:

Typen beeinflussen fast alle Aspekte des Objektverhaltens. Sogar die Bedeutung der Objektidentität wird in gewissem Sinne beeinflusst: Bei unveränderlichen Typen können Operationen , die neue Werte berechnen, tatsächlich einen Verweis auf ein vorhandenes Objekt mit demselben Typ und Wert zurückgeben, während dies für veränderbare Objekte nicht zulässig ist . ZB nach a = 1; b = 1, a und b können sich je nach Implementierung auf dasselbe Objekt mit dem Wert Eins beziehen oder nicht, jedoch nach c = []; d = [], c und d beziehen sich garantiert auf zwei verschiedene, eindeutige, neu erstellte leere Listen. (Beachten Sie, dass c = d = [] sowohl c als auch d dasselbe Objekt zuweist.)

Aus der obigen Aussage können wir schließen, dass die Zeichenfolgen, die ein unveränderlicher Typ sind, möglicherweise fehlschlagen, wenn sie mit "is" überprüft werden, und erfolgreich sein können, wenn sie mit "is" überprüft werden.

Gleiches gilt für int, tuple, die ebenfalls unveränderliche Typen sind

RAM
quelle
8

Die ==Äquivalenz des Bedienertestwerts. Der isOperator testet die Objektidentität, Python testet, ob die beiden wirklich dasselbe Objekt sind (dh an derselben Adresse im Speicher leben).

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

In diesem Beispiel hat Python nur ein Zeichenfolgenobjekt und beide erstellt aund bverweist darauf. Der Grund dafür ist, dass Python einige Zeichenfolgen intern zwischenspeichert und als Optimierung wiederverwendet. Es gibt wirklich nur eine Zeichenfolge 'Banane' im Speicher, die von a und b gemeinsam genutzt wird. Um das normale Verhalten auszulösen, müssen Sie längere Zeichenfolgen verwenden:

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

Wenn Sie zwei Listen erstellen, erhalten Sie zwei Objekte:

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

In diesem Fall würden wir sagen, dass die beiden Listen äquivalent sind, weil sie dieselben Elemente haben, aber nicht identisch, weil sie nicht dasselbe Objekt sind. Wenn zwei Objekte identisch sind, sind sie auch äquivalent, aber wenn sie äquivalent sind, sind sie nicht unbedingt identisch.

Wenn asich ein Objekt auf ein Objekt bezieht und Sie es zuweisen b = a, beziehen sich beide Variablen auf dasselbe Objekt:

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

isvergleicht den Speicherort. Es wird für den Vergleich auf Objektebene verwendet.

==vergleicht die Variablen im Programm. Es wird zur Überprüfung auf Wertebene verwendet.

is prüft auf Äquivalenz der Adressenebene

== prüft auf Wertäquivalenz

Johnashu
quelle
3

isist Identitätstest, ==ist Gleichheitstest (siehe Python-Dokumentation ).

In den meisten Fällen, wenn a is b, dann a == b. Es gibt jedoch Ausnahmen, zum Beispiel:

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

Sie können also nur isfür Identitätstests verwenden, niemals für Gleichheitstests.

Ryan
quelle