Everything is a object question

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
ferdi09
User
Beiträge: 16
Registriert: Donnerstag 18. Januar 2007, 11:23

Freitag 20. April 2007, 20:59

Hallo,
kann mir jemand folgendes Phänomen in Python erklären:

>>> 2 ** 10 is 2 ** 10
False
>>> 2 ** 5 is 2 ** 5
True

Ich bekomme keine Antwort darauf.

Gruss
Holger
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Freitag 20. April 2007, 21:35

Das ist ein Implementationsdetail von CPython.

"is" überprüft auf Identität, also ob es sich um dasselbe Objekt handelt. In der Regel nutzt man es nur bei "is None", sonst prüft man immer auf Gleichheit.

Wenn du z.B. 17 schreibst, erzeugt Python eine Instanz der Klasse int, die den Wert 17 repräsentiert und - um es mal etwas ungenau zu formulieren - bindet dieses Objekt an die 17.

Jetzt ist es etwas ineffizient, jedesmal ein solches Objekt zu instanzzieren. Deshalb cached CPython die Instanzen aller Integer von 0 bis 99, und kann so jedesmal, wenn z.B. eine 17 auftaucht, dasselbe Objekt verwenden. Daher ist "17 ist 17" wahr, aber "100 is 100" falsch, da im letzten Falle zweimal eine Instanz erzeugt wird.

2**5 = 32 und damit < 100
2**10 > 100

Daher das Verhalten, das wie gesagt Implementationsspezifisch ist und in Jython oder PyPy z.B. ganz anders sein kann.

PS: Interessanterweise scheint Python 2.4 bei verwendung in einer Anweisung zu cachen, so dass z.B. "1000 is 1000" jetzt wahr ist, aber

Code: Alles auswählen

>>> a = 1000
>>> b = 1000
>>> a is b
False
>>> a = 99
>>> b = 99
>>> a is b
True
Lange Rede, kurzer Sinn: "is" prüft auf Identität, "==" auf Gleichheit, und das will man in der Regel
ferdi09
User
Beiträge: 16
Registriert: Donnerstag 18. Januar 2007, 11:23

Freitag 20. April 2007, 21:43

Hm danke für den Beitrag. Das verstehe ich auch soweit.
Allerdings zweifele ich ein wenig an der Erklärung. Denn danach müsste ja
>>> 1024 is 1024
auch False ergeben. Tut es aber nicht. Das ergibt True.
Der Aufruf 2**10 is 2**10 liefert zwei unterschiedliche Object Ids zurück, das ist soweit zweifelsfrei. Warum liefert 1024 is 1024 nur eine zurück?
BlackJack

Freitag 20. April 2007, 22:30

Weil der Compiler bemerkt das Du hier in einer Zeile zweimal das gleiche Literal verwendet hast gibt's das Objekt nur einmal.
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Freitag 20. April 2007, 22:46

BlackJack hat geschrieben:Weil der Compiler bemerkt das Du hier in einer Zeile zweimal das gleiche Literal verwendet hast gibt's das Objekt nur einmal.
Für vergleiche in einer Zeile mag es zustimmen, es erklärt leider noch nicht ganz, warum ...

Code: Alles auswählen

>>> a = 100
>>> b = 100
>>> a is b
False
>>> 
>>> x = 99
>>> y = 99
>>> x is y
True
... ist. Habe ein paar Werte nur getestet, aber es scheint so, dass bei Werten die >= 100 oder <0 sind, die "is" comparsion False und bei Werten von z.B. 0-99 True ist.

( ...wie gesagt, nur mit ein paar Werten getestet ... vielleicht wiederlegt es ja jemand ;) ... )


Tjoa, was "comparsions" angeht, so hat Python ja schon in folgendem Bug Issue bewiesen, dass nicht vielleicht alles Gold ist was glänzt: https://sourceforge.net/tracker/index.p ... tid=105470

Probiert man folgendes, so verhält es sich wie erwartet/gewünscht:

Code: Alles auswählen

>>> a = int(2**10)
>>> b = int(2**10)
>>> a is b
False
>>> a = int(1024)
>>> b = int(1024)
>>> a is b
False
Leider jedoch folgendes nicht:

Code: Alles auswählen

>>> s = int(99)
>>> t = int(99)
>>> s is t
True
Die Zahlen von 0-99 sind scheinbar ein wenig verhext... it's not a bug, it's magic *hehehe*.

>>Masaru<<
BlackJack

Freitag 20. April 2007, 23:19

Es ist tatsächlich ein Feature und kein Bug. Deine Erwartungshaltung ist falsch. ``is`` testet auf Objektidentität, nicht auf Gleichheit. Ob und wenn ja wann Integerobjekte mit dem gleichen Wert auch das selbe Objekt sind, ist nirgends definiert. Das ist ein Implementierungsdetail. Wie man hier schön sehen kann:

Code: Alles auswählen

>>> a = 100
>>> b = 100
>>> a is b
True
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Freitag 20. April 2007, 23:35

... damit hast du wohl recht.

Ich (und ich glaube, dass ich nicht damit alleine war/bin) habe erwartet, dass ein erzeugtes Objekt int(5) und weiteres int(5) zwei unterschiedliche Objekte letztendlich auch sind.

Kennt man jedoch dieses "Feature" und die C-Implementierung des Integers in Python, so wirkt es dann nicht mehr so ganz irritierend ... schön oder beruhigender ist es damit in meinen Augen jedoch weiterhin nicht :?.

Integer haben in der Python Object Implementierung ein Feature welches erlaubt, SmallInteger (diese können unterschiedlich definiert sein .. 0-100, 5-257, .. hab ein wenig die C Sourcecodes verschiedener Builds durchstöbert und verschiedene gefunden) zu cashen und zu teilen.

Um genauer zu sein, werden die "Referrenzen" dessen Wert innerhalb der "relativ" kleinen Range liegt, in einem Array gespeichert.

Vorteil ... tjoa ... Zahlen von z.B. 0-100 nutzt man direkt und noch stärker indirekt häufiger als man denkt. Vermutlich dient es entsprechend der Performance- und Speicheroptimierung.

Gängige Rechnungen, Iterationen, Listen-Aufbauten, etc. pp. ... diese Werte durchrauschen Python-Scripte/Programme mit großer Wahrscheinlichkeit am laufennden Band wie PKWs die Hauptstrassen einer Großstadt zur RushHour ;).

Das auf unseren Systemen diese "Range" unterschiedlich definiert ist, wird an der Weiterentwicklung und an der Justierung auf die unterschiedlichen Hardware- und Betriebssystemumgebungen liegen, auf denen ja die darauf hin optimiert kompilierten Python Interpreter laufen.

Nachteil ... wie wir schon festgestellt haben ... wenn natürlich aber dann auf der anderen Seite zwei oder mehrere Integer PyObjekte verschiedenen Variablen-Namens jedoch auf auf ein und denselben Bereich zeigen, weil sie wie es so schön heisst "geshared" werden ... dann erklärt es auch das "True" Ergebnis auf den Vergleich der Objekt Identität.

Dann ist

Code: Alles auswählen

>>> a = int(5)
>>> b = int(5)
>>> a is b
True
während das gleiche Spiel mit einem weit aus höheren Wert wie z.B. 500 "False" ergeben würde.

>>Masaru<<
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Samstag 21. April 2007, 10:14

Masaru hat geschrieben: ... ist. Habe ein paar Werte nur getestet, aber es scheint so, dass bei Werten die >= 100 oder <0 sind, die "is" comparsion False und bei Werten von z.B. 0-99 True ist.
Ähh... Genau das habe ich doch oben geschrieben, oder was es zu unverständlich?

In CPython gibt es immer nur eine Instanz der Zahlen 0 bis 99, alle anderen Zahlen werden jeweils an neue Instanzen gebunden
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Samstag 21. April 2007, 10:39

Masaru hat geschrieben: Nachteil ... wie wir schon festgestellt haben ... wenn natürlich aber dann auf der anderen Seite zwei oder mehrere Integer PyObjekte verschiedenen Variablen-Namens jedoch auf auf ein und denselben Bereich zeigen, weil sie wie es so schön heisst "geshared" werden ... dann erklärt es auch das "True" Ergebnis auf den Vergleich der Objekt Identität.
Du hast es immer noch nicht kapiert. Das ist kein Nachteil. Integers (und andere immutable Werte) *testet* *man* *nicht* mit `is` auf Gleichheit.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Montag 23. April 2007, 14:32

@Joghurt: Doch doch :D, hattest du auch so geschrieben, und hatte ich auch so verstanden ... wobei es nicht immer (wie ja auch von BlackJack es als "Implementierungsdetail" beschrieben wurde) der Wertebereich zwischen 0 und 99 sein muss.
Der Bereich ist davon abhängig, wie es im Python C-Code definiert ist. Und das kann, wie man ja bei BlackJacks Beispiel sah, schwanken.
Ein bisschen drum rumgeschwafelt habe ich ja auch, gebe ich ja auch zu *g* ;).

@Birkenfeld: Das man nicht mit "is" auf Gleichheit von Integern prüft, da hast du und auch schon die anderen Vorredner natürlich recht.
Ich habe übrigends auch nicht gesagt, dass ich "Integers auf die Gleichheit teste", sondern - worum es sich in diesem Thread ja auch dreht - den "...Vergleich der Objekt Identität..." prüfe, ergo die Objekte an sich (und nicht die Werte) vergleiche.

Meine "Nachteil" Aussage war vielleicht nicht gerade die diplomatisch gewählteste. Es ist mehr auch meine eigene, subjektive Meinung zu dem Thema, als ein Tatsache. Okay okay *g*, nehme ich hiermit zurück.

Dein Wink auf das Konzept "unveränderliche Objekte (immutable Objects)" finde ich (wenn auch ein wenig forsch rübergebracht ;) ) als ein guten Punkt, dieses von ferdi09 erfragte und auf den ersten Blick nicht mit gegebenen Python Mitteln und Testszenarien zu erklärende Phänomen zu entschlüsseln.

Leider war es nur lediglich in den Raum geschossen, ohne Erklärung, Links oder Verdeutlichung an der Python-Essenz selbst :cry:.

Vielleicht könntest du ja aus dieser Perspektive noch einmal genauer das Phänomen aufgreifen?

>>Masaru<<
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Montag 23. April 2007, 17:12

Naja, der Grundgedanke ist einfach, dass unveränderliche ("immutable") Objekte sich eben nicht ändern, und deshalb muss der Interpreter nicht jedes Mal ein neues solches erzeugen, sondern kann eine neue Referenz auf ein bestehendes zurückgeben. Das gleiche passiert z.B. bei Strings mit Länge 1 oder dem leeren Tupel.

Bei veränderlichen Objekten, sagen wir mal, der leeren Liste, geht das natürlich nicht.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Antworten