"ist" - operator verhält sich unerwartet mit Ganzzahlen
Warum hat die folgenden Verhalten sich unerwartet in Python?
>>> a = 256
>>> b = 256
>>> a is b
True # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False # What happened here? Why is this False?
>>> 257 is 257
True # Yet the literal numbers compare properly
Ich bin mit Python-2.5.2. Versuchen Sie einige verschiedene Versionen von Python, es wird angezeigt, dass Python 2.3.3 zeigt das obige Verhalten zwischen 99 und 100.
Basierend auf den oben genannten, kann ich vermuten, dass Python ist intern umgesetzt, so dass "kleine" ganze zahlen werden gespeichert in einer anderen Weise als größere Ganzzahlen und die is
Betreiber kann sagen, der Unterschied. Warum der undicht Abstraktion? Was ist eine bessere Möglichkeit zum Vergleich zweier beliebiger Objekte, um zu sehen, ob Sie identisch sind, wenn ich nicht im Voraus wissen, ob Sie zahlen oder nicht?
InformationsquelleAutor Greg Hewgill | 2008-11-20
Du musst angemeldet sein, um einen Kommentar abzugeben.
Werfen Sie einen Blick auf diese:
EDIT: Hier ist was ich gefunden habe in den Python-2-Dokumentation, "Reine Integer-Objekte" (Es ist das gleiche für Python 3):
Die -5 ist nur eine Heuristik, um zu erfassen üblichen negativen Platzhalter, denke ich. 0..255 umfasst arrays der einzelnen byte-Werte. Es ist 256 das ist geheimnisvoll, aber ich denke, es ist für die (de -) Montage von ganzen zahlen in bytes.
Von dem, was ich verstehe, der Bereich wurde gewählt, indem man die Häufig verwendeten Werte in mehreren Projekten (und mehrere Sprachen).
Nach reddit.com/r/Python/comments/18leav/..., die Bereich verwendet werden [-5,100]. Es wurde erweitert, um umfassen die gesamte Palette der byte-Werte - plus-256, denn das ist vermutlich eine gemeinsame Nummer.
InformationsquelleAutor Cybis
Zusammenfassend - lassen Sie mich betonen: nicht verwenden
is
zu vergleichen ganze zahlen.Dies ist nicht Verhalten sollten Sie keine Erwartungen über.
Verwenden Sie stattdessen
==
und!=
zu vergleichen, für Gleichheit und Ungleichheit, beziehungsweise. Zum Beispiel:Erklärung
Dies wissen, Sie müssen Folgendes wissen.
Erste, was bedeutet
is
tun? Es ist ein Vergleichsoperator. Aus der Dokumentation:Und so die folgenden sind äquivalent.
Aus der Dokumentation:
Beachten Sie, dass die Tatsache, dass die id des Objekts in CPython (die Referenz-Implementierung von Python) ist der Ort im Speicher ist eine Implementierung detail. Andere Implementierungen von Python (wie Jython oder IronPython) konnte problemlos eine andere Implementierung für
id
.Also, was ist der use-case für
is
? PEP8 beschreibt:Die Frage
Fragen Sie, und des Staates, die folgende Frage (mit code):
Ist es nicht das erwartete Ergebnis. Warum ist es so erwartet? Es bedeutet nur, dass die ganzen zahlen bewertet
256
verwiesen wird, die von beidena
undb
sind die gleichen integer-Instanz. Ganze zahlen sind unveränderlich in Python, so dass Sie nicht ändern können. Dies sollte keine Auswirkungen auf code. Es sollte nicht erwartet werden. Es ist lediglich eine Implementierung detail.Aber vielleicht sollten wir froh sein, dass es nicht nur einen neuen, separaten Instanz im Speicher jedes mal, wenn wir Zustand einen Wert von 256.
Sieht aus wie wir haben jetzt zwei separate Instanzen von Integer-zahlen mit dem Wert der
257
im Speicher. Da ganze zahlen sind unveränderlich, diese Abfälle Speicher. Hoffen wir, dass wir nicht verschwenden eine Menge davon. Wahrscheinlich sind wir nicht. Aber dieses Verhalten ist nicht gewährleistet.Gut, das sieht aus wie Ihre jeweilige Implementierung von Python versucht, schlau zu sein und nicht Redundant bewertet Ganzzahlen im Speicher, es sei denn, es muss. Sie scheinen zu zeigen, Sie mit den Referenten-Implementierung von Python, die ist CPython. Gut für CPython.
Könnte es sogar besser sein, wenn CPython könnte dies Global tun, wenn es tun konnte, so Billig (als würde es Kosten in der Suche), vielleicht eine andere Implementierung könnte.
Aber für Auswirkungen auf den code haben, sollten Sie sich nicht sorgen, wenn eine ganze Zahl ist eine bestimmte Instanz eines integer. Sollten Sie nur interessieren, was den Wert dieser Instanz ist, und verwenden Sie die normalen Vergleichsoperatoren, d.h.
==
.Was
is
hatis
überprüft, dass dieid
zwei Objekte gleich sind. In CPython, dieid
ist der Ort im Speicher, aber es könnte einige andere eindeutig identifizierende Nummer in einer anderen Umsetzung. Um diese aufgeführt mit code:ist das gleiche wie
Warum würden wir wollen
is
dann?Kann dies eine sehr schnelle Kontrolle relativ zu sagen, die überprüfen, ob zwei sehr lange strings sind gleich Wert. Aber da es gilt, die Einzigartigkeit des Objekts, so haben wir begrenzte Anwendungsfälle. In der Tat, wir haben meistens wollen es verwenden, um zu überprüfen, für
None
, das ist ein singleton (eine einzige Instanz vorhandene in einem Ort im Speicher). Wir erstellen die anderen singletons, wenn Gefahr besteht, zu verschmelzen, was wir überprüfen konnten, mitis
, aber diese sind relativ selten. Hier ist ein Beispiel (in Python 2 und 3) z.B.Ausgegeben:
Und so sehen wir, mit
is
und ein sentinel, wir sind in der Lage zu unterscheiden, wennbar
wird ohne Argumente aufgerufen und beim Aufruf mitNone
. Diese sind die primären use-cases füris
- tun nicht es verwenden, um test für die Gleichheit von zahlen, strings, Tupel, oder andere Dinge wie diese.is
- verwenden Sie nicht, um test für die Gleichheit von zahlen, strings, Tupel, oder andere Dinge wie diese." Aber ich versuche mich zu integrieren, die eine einfache state-machine in meine Klasse, und da die Staaten undurchsichtig sind Werte, deren einzige beobachtbare Eigenschaft ist die, identisch oder Verschieden, es sieht ganz natürlich für Sie zu sein, vergleichbar mitis
. Ich Plane, verwenden Sie interniert Zeichenfolgen als Staaten. Ich hätte es vorgezogen einfachen Ganzzahlen, aber leider ist Python nicht intern Ganzzahlen (0 is 0
ist eine Implementierung detail).klingt wie Sie brauchen enums? stackoverflow.com/questions/37601644/...
Vielleicht, vielen Dank, wusste nichts von Ihnen. Dies könnte durch eine entsprechende Ergänzung, um Sie zu beantworten IMO.
Vielleicht mit einer Reihe von stummen Objekte wie die sentinel in Ihre Antwort wäre eine leichte Lösung...
enums sind in der Python-3-standard-Bibliothek, und das würde wahrscheinlich ermutigen Sie Ihre code, um ein bisschen mehr Aussagen als bare-die Wächter.
InformationsquelleAutor Aaron Hall
Es hängt davon ab, ob Sie schauen, um zu sehen, wenn 2 Dinge gleich sind, oder das gleiche Objekt.
is
überprüft, um zu sehen, wenn Sie das gleiche Objekt, nicht nur gleich. Der kleine int-Werte sind wahrscheinlich auf das gleiche Speicherstelle für die Raum-EffizienzSollten Sie verwenden
==
zu vergleichen, die Gleichheit von beliebigen Objekten. Sie können festlegen, dass das Verhalten mit der__eq__
, und__ne__
Attribute.InformationsquelleAutor JimB
Ich bin spät aber doch, möchten Sie eine Quelle mit der Antwort?*
Gute Sache über CPython ist, dass man tatsächlich sehen kann, die Quelle für diese. Ich werde die links für die
3.5
release für jetzt; finden Sie die entsprechenden2.x
lieben, ist trivial.In CPython, die
C-API
- Funktion, mit der Schaffung einer neuenint
Objekt istPyLong_FromLong(long v)
. Die Beschreibung dieser Funktion ist:Weiß nicht, über Sie, aber ich sehe das und denke: finden wir, dass array!
Wenn Sie noch nicht bedacht mit der
C
code Implementierung CPython sollten Sie, ist alles ziemlich organisiert und gut lesbar. Für unseren Fall müssen wir einen Blick in die- Objekte/
- Unterverzeichnis der Haupt-Quellcode-directory-Baum.PyLong_FromLong
befasst sich mitlong
Objekte, so sollte es nicht schwer zu folgern, dass müssen wir gucken inslongobject.c
. Nach der Suche im inneren könnte man denken, die Dinge sind chaotisch, Sie sind, aber Angst nicht, die Funktion die wir suchen, ist chillen aufline 230
auf uns warten, check it out. Es ist ein kleineres, so dass die Funktion main body (ohne Erklärungen) ist leicht eingefügt hier:Nun, wir sind keine
C
master-code-haxxorz aber wir sind auch nicht dumm, wir können sehen, dassCHECK_SMALL_INT(ival);
gucken bei uns alle verführerisch; wir können verstehen, es hat etwas mit diesem zu tun. Lassen Sie uns check it out:Es ist also ein makro, das ruft die Funktion
get_small_int
wenn der Wertival
erfüllt die Bedingung:Also, was sind
NSMALLNEGINTS
undNSMALLPOSINTS
? Wenn Sie Ahnen, dass Makros, die Sie nichts bekommen, weil das war nicht so eine schwere Frage.. Wie auch immer, hier sind Sie:Also unsere Bedingung ist
if (-5 <= ival && ival < 257)
nennenget_small_int
.Keinen anderen Ort zu gehen, sondern setzen unsere Reise Fort durch den Blick auf
get_small_int
in seiner ganzen Pracht (gut, wir betrachten es nur den physischen Körper, weil das waren die interessanten Dinge sind):Okay, deklarieren Sie eine
PyObject
behaupten, dass der frühere Zustand hält und das ausführen des Auftrags:small_ints
sieht viel wie das array haben wir gesucht.. und es geht!!! Wir könnte gerade gelesen habe das verdammte Dokumentation und hätten wir wissen alle zusammen!:Also ja, das ist unser Mann. Wenn Sie möchten, erstellen Sie eine neue
int
im Bereich[NSMALLNEGINTS, NSMALLPOSINTS)
dann bekommst du nur wieder einen Verweis auf ein bereits bestehendes Objekt wurde reserviert.Da sich der Verweis bezieht sich auf das gleiche Objekt, die Ausstellung
id()
direkt oder bei der überprüfung auf Identität mitis
auf wird es wieder genau das gleiche.Aber, Wann werden Sie verteilt??
Während der Initialisierung in
_PyLong_Init
Python wird gerne in einer for-Schleife tun dies für Sie tun:Ich hoffe meine Erklärung hat Euch
C
(Wortspiel offensichtlich intented) die Dinge klar jetzt.Aber, 257 ist 257? What ' s up?
Dies ist tatsächlich einfacher, zu erklären, und ich habe versucht, es so schon tun; es ist aufgrund der Tatsache, dass Python ausführen dieser interaktiven Kontoauszug:
als einen einzigen block. Während complilation von dieser Aussage, CPython werden sehen, dass Sie haben zwei passende Literale und nutzen den gleichen
PyLongObject
vertreten257
. Sie können sehen, wenn Sie die Zusammenstellung selbst und seinen Inhalt untersuchen:Wenn CPython übernimmt den Betrieb; es ist jetzt nur zu laden, die genau dasselbe Objekt:
So
is
zurückTrue
.* -- ich werde versuchen, dieses Wort in einem mehr einführenden Art und Weise, um für die meisten in der Lage sein zu Folgen.
InformationsquelleAutor Jim Fasarakis Hilliard
Wie können Sie überprüfen, in Quell-Datei (intobject.c, Python-caches kleine ganze zahlen für Effizienz. Jedes mal, wenn Sie erstellen einen Verweis auf eine kleine integer-Zahl, Sie sind bezogen auf die zwischengespeicherten kleinen integer", nicht ein neues Objekt. 257 ist nicht eine kleine ganze Zahl, so wird Sie berechnet, indem ein anderes Objekt.
Ist es besser
==
für diesen Zweck.InformationsquelleAutor Angel
Ich denke, dass Sie Ihre Hypothesen richtig ist. Experimentieren Sie mit
id
(Identitäts-Objekt):Scheint es, dass die zahlen
<= 255
werden als Literale behandelt und alles über anders behandelt!InformationsquelleAutor Amit
Für unveränderliche Werte-Objekte, wie ints, strings oder datetimes -, Objekt-Identität ist dabei nicht besonders hilfreich. Es ist besser, zu denken über Gleichheit. Identität ist im wesentlichen eine Implementierung detail für value-Objekte - denn Sie sind unveränderlich, es gibt keine wirksamen Unterschied zwischen müssen mehrere Referenzen auf das gleiche Objekt oder mehrere Objekte.
InformationsquelleAutor babbageclunk
is
ist die Identität Gleichheits-operator (Funktion wieid(a) == id(b)
); es ist nur so, dass zwei gleiche zahlen sind nicht unbedingt das gleiche Objekt. Aus performance-Gründen einige kleine ganze zahlen zufällig memoized, so dass Sie neigen dazu, die gleichen (dies getan werden kann, da Sie unveränderlich sind).PHP ist
===
Betreiber, auf der anderen Seite, wird beschrieben, wie die überprüfung der Gleichheit und der Typ:x == y and type(x) == type(y)
als pro Paulo Freitas' Kommentar. Dies wird genügen, für häufige zahlen, aber unterscheiden sich vonis
für Klassen definieren__eq__
in eine absurde Art und Weise:PHP anscheinend können die gleiche Sache für die "built-in" Klassen (die ich ergreifen, um meine Umsetzung auf C-Ebene, nicht in PHP). Etwas weniger absurd Nutzung könnte ein timer-Objekt, das einen anderen Wert jedes mal, wenn es verwendet wird, als eine Zahl. Ganz, warum Sie wollen, zu emulieren, Visual Basic
Now
anstatt zu zeigen, dass es eine Auswertung mittime.time()
weiß ich nicht.Greg Hewgill (OP) machte eine Klärung der Kommentar "Mein Ziel ist ein Vergleich der Objekt-Identität, eher als Gleichheit von Wert. Außer für zahlen, wo ich will, zur Behandlung von Objekt-Identität das gleiche wie Gleichheit von Wert."
Diese hätte noch eine andere Lösung, wie wir kategorisieren die Dinge, wie zahlen oder nicht, zu wählen, ob wir vergleichen mit
==
oderis
. CPython definiert die Anzahl-Protokoll, einschließlich PyNumber_Check, aber dies ist nicht zugänglich von Python selbst.Wir könnten versuchen, es zu verwenden
isinstance
mit der ganzen Anzahl Arten die wir kennen, aber das wäre zwangsläufig unvollständig. Die Arten-Modul enthält eine StringTypes Liste, aber keine NumberTypes. Seit Python 2.6, die eingebaute Anzahl von Klassen eine Basisklassezahlen.Zahl
, aber es hat das gleiche problem:Durch die Art und Weise, NumPy produzieren separaten Instanzen von niedrigen zahlen.
Ich weiß wirklich nicht, eine Antwort auf diese Variante der Frage. Ich nehme an, könnte man theoretisch verwenden ctypes zu nennen
PyNumber_Check
, aber auch, dass die Funktion debattiert worden, und es ist sicherlich nicht tragbar. Wir müssen einfach weniger werden, insbesondere über das, was wir testen für jetzt.Am Ende dieser Ausgabe stammt von der Python, die ursprünglich nicht mit einem Typ-tree mit Prädikaten wie Regelung
number?
oder Haskell ist Typ Klasse Num.is
überprüft, Objekt-Identität, nicht-Wert-Gleichheit. PHP hat eine bunte Geschichte, wo===
verhält sich wieis
nur auf Objekte in PHP5, sondern PHP4 nicht. Sie sind es, die wachsenden Schmerzen, die das bewegen über Sprachen (einschließlich Versionen).InformationsquelleAutor Yann Vernier
Gibt es ein weiteres Problem, das nicht bereits in eine der vorhandenen Antworten. Python erlaubt ist, verschmelzen alle zwei unverrückbare Werte, und pre-erstellt kleine int-Werte sind nicht der einzige Weg dies geschehen kann. Eine Python-Implementierung ist nie garantiert um dies zu tun, aber Sie alle tun es für mehr als nur kleine ints.
Für eine Sache, es gibt einige andere zuvor erstellten Werte, wie in der leere
tuple
,str
, undbytes
, und einige kurze Zeichenfolgen (in CPython 3.6, es ist die 256 Zeichen der Latin-1-strings). Zum Beispiel:Aber auch nicht vorab erstellten Werte können identisch sein. Betrachten Sie diese Beispiele:
- Und das beschränkt sich nicht auf
int
Werte:Offensichtlich, CPython kommt nicht mit einem bereits erstellten
float
Wert für42.23e100
. Also, was ist hier Los?Den CPython-compiler verschmelzen die Konstanten Werte von einigen bekannten unveränderlichen Typen wie
int
,float
,str
,bytes
in der selben compilation unit. Für ein Modul, das gesamte Modul ist eine compilation-unit, aber bei den interaktiven interpreter, jede Anweisung ist eine separate compilation unit. Dac
undd
definiert, die in separaten Aussagen, deren Werte noch nicht zusammengeführt. Dae
undf
definiert sind, in der gleichen Anweisung, Ihre Werte zusammengeführt.Können Sie sehen, was Los ist durch das zerlegen der Bytecodes. Versuchen Sie eine Funktion definieren, die nicht
e, f = 128, 128
und dann ruftdis.dis
auf Sie, und du wirst sehen, dass es einen einzigen Konstanten Wert(128, 128)
Können Sie feststellen, dass der compiler gespeichert hat
128
als Konstante, obwohl es nicht von der bytecode, das gibt Ihnen eine Vorstellung davon, wie wenig Optimierung CPython-compiler tut. Das bedeutet, dass die (nicht-leere) - Tupeln tatsächlich nicht am Ende zusammengeführt:Setzen, dass in einer Funktion
dis
es, und der Blick auf dieco_consts
—es gibt eine1
und ein2
zwei(1, 2)
Tupeln, die die gleichen1
und2
aber nicht identisch, und eine((1, 2), (1, 2))
Tupel, die gleich zwei verschiedene Tupel.Gibt es noch eine weitere Optimierung, mit der CPython: string-ein Praktikum. Im Gegensatz zu compiler-Konstanten-Faltung, dieses ist nicht beschränkt auf Quellcode-Literale:
Auf der anderen Seite, es ist beschränkt auf die
str
geben, und die Saiten an interner Speicher Art "ascii-kompakt", "kompakt" oder "legacy-ready", und in vielen Fällen nur "ascii-kompakt" erhalten interniert.Jedenfalls, die Regeln, für welche Werte sein muss, sein könnte, oder kann unterschiedlich sein, variieren von Implementierung zu Implementierung, und zwischen Versionen der gleichen Umsetzung, und vielleicht auch zwischen läuft der gleiche code auf die gleiche Kopie von der gleichen Umsetzung.
Kann es sinnvoll sein, das erlernen der Regeln für ein bestimmtes Python für den Spaß von ihm. Aber es lohnt sich nicht sich auf Sie in Ihrem code. Die einzig sichere Regel ist:
Oder, in anderen Worten, verwenden Sie nur
is
zu testen, für die dokumentiert singletons (wieNone
) oder werden nur an einer Stelle im code (wie die_sentinel = object()
idiom).InformationsquelleAutor abarnert
Passiert das auch mit strings:
Jetzt scheint alles in Ordnung.
Das ist zu erwarten.
Nun, das ist unerwartet.
'xx'
ist wie erwartet, wie ist'xxx'
, aber'x x'
ist nicht.Das ist, weil es aussieht wie ein symbol, wenn es keinen Platz in ihm. Namen automatisch interniert, also wenn es etwas gibt, benannt
xx
überall in Ihrem Python-session, dass string bereits interniert; und es könnte eine Heuristik, die es tut, wenn er sich nur erinnert einen Namen. Wie mit zahlen, diese kann getan werden, weil Sie unveränderlich sind. docs.python.org/2/library/functions.html#intern guilload.com/python-string-interningInformationsquelleAutor sobolevn