2010-05-18 10 views
56

diesen Code vor:Warum hat das Schlüsselwort "is" ein anderes Verhalten, wenn ein Punkt in der Zeichenfolge vorhanden ist?

>>> x = "google" 
>>> x is "google" 
True 
>>> x = "google.com" 
>>> x is "google.com" 
False 
>>> 

Warum ist es so?

Um sicherzustellen, dass das obige korrekt ist, habe ich gerade auf Python 2.5.4, 2.6.5, 2.7b2, Python 3.1 auf Windows und Python 2.7b1 auf Linux getestet.

Es sieht so aus als ob es Konsistenz über alle von ihnen gibt, so ist es von Entwurf. Fehle ich etwas?

Ich finde es nur heraus, dass einige meiner persönlichen Domain-Filter-Skript damit versagt.

+10

Schöner Fang. Das ist seltsam. –

+2

Beide sind in Python 2.5.2 "Falsch". –

+3

In ActivePython 2.5.4.4 werden die gleichen Ergebnisse wie für OP angezeigt. Hat mit Sicherheit etwas mit String Interning zu tun, oder? –

Antwort

89

is prüft Objektidentität und jede Implementierung von Python, wenn es buchstäbliche von unveränderlichen Typen trifft, ist vollkommen frei zu entweder ein neues Objekt dieses unveränderlichen Typs machen, oder suchen durch bestehende Objekte dieses Typs Prüfen Sie, ob einige von ihnen wiederverwendet werden können (indem Sie einen neuen Verweis auf dasselbe zugrunde liegende Objekt hinzufügen). Dies ist eine pragmatische Wahl der Optimierung und nicht unterliegen semantischen Einschränkungen, so sollte Ihr Code nie darauf angewiesen, welchen Weg eine Give-Implementierung nehmen kann (oder es könnte mit einem Bugfix/Optimierung Release von Python brechen!).

zum Beispiel vor:

>>> import dis 
>>> def f(): 
... x = 'google.com' 
... return x is 'google.com' 
... 
>>> dis.dis(f) 
    2   0 LOAD_CONST    1 ('google.com') 
       3 STORE_FAST    0 (x) 

    3   6 LOAD_FAST    0 (x) 
       9 LOAD_CONST    1 ('google.com') 
      12 COMPARE_OP    8 (is) 
      15 RETURN_VALUE  

so in dieser speziellen Implementierung, innerhalb einer Funktion, ist Ihre Beobachtung nicht gelten und nur ein Objekt für die wörtliche (Wörtliche) gemacht, und zwar:

>>> f() 
True 

das ist Pragmatisch, weil innerhalb einer Funktion einen durch~~POS=TRUNC durch die lokale Tabelle von konstanten zu machen (zu etwas Speicher zu sparen, indem Sie nicht mehrere konstante unveränderliche Objekte zu machen, wo man genügt) ist ziemlich billig und schnell, und kann eine gute Leistungsrendite bieten, da die Funktion danach wiederholt aufgerufen werden kann.

Aber, ganz gleich Implementierung an der interaktiven Eingabeaufforderung (bearbeiten: Ich dachte ursprünglich dies auch auf einem Moduls oberster Ebene passieren würde, aber ein Kommentar von @Thomas stellte ich später rechts sehen):

macht sich nicht die Mühe, Speicher auf diese Weise zu speichern - die id s sind unterschiedlich, dh unterschiedliche Objekte. Es gibt potentiell höhere Kosten und niedrigere Erträge und so sagen die Heuristiken des Optimierers dieser Implementierung, dass sie die Suche nicht stören und einfach weitermachen.

bearbeiten: auf Modul oberster Ebene, pro @Thomas' Beobachtung, da zB:

$ cat aaa.py 
x = 'google.com' 
y = 'google.com' 
print id(x), id(y) 

wieder sehen wir die Tisch-of-Konstanten-basierte Speicher-Optimierung in dieser Implementierung:

>>> import aaa 
4291104 4291104 

(Ende von Edit per @Thomas Beobachtung).

schließlich wieder auf der gleichen Umsetzung:

>>> x = 'google' 
>>> y = 'google' 
>>> id(x), id(y) 
(2484672, 2484672) 

die Heuristiken sind hier anders, weil die Zeichenkette „sieht aus wie könnte es ein Bezeichner sein“ - so könnte es Internierung in Betrieb erfordern, verwendet werden .. Englisch: www.mjfriendship.de/en/index.php?op...39&Itemid=32 So trainiert der Optimierer es trotzdem (und wenn man einmal interniert ist, wird es natürlich sehr schnell) Und in der Tat, was für eine Überraschung ...:

>>> z = intern(x) 
>>> id(z) 
2484672 

... xhat gewesen intern das erste Mal ed (wie Sie sehen, ist der Rückgabewert von intern ist das gleiche Objekt als x und y , wie es das gleiche hat id()). Natürlich sollten Sie sich auch nicht darauf verlassen - der Optimierer hat nicht automatisch alles zu internieren, es ist nur eine Optimierungsheuristik; Wenn Sie intern ed String, intern explizit benötigen, nur um sicher zu sein. Wenn Sie explizit intern Strings tun ...:

>>> x = intern('google.com') 
>>> y = intern('google.com') 
>>> id(x), id(y) 
(4213000, 4213000) 

... dann Sie tun sicherzustellen, die genau das gleiche Objekt (dh gleiche id()) Ergebnisse jedes Mal - so können Sie micro anwenden Optimierungen wie das Überprüfen mit is statt == (ich habe den winzigen Leistungsgewinn kaum je gefunden, der Aufwand lohnt sich ;-).

bearbeiten: nur zu klären, sind hier die Art der Leistungsunterschiede ich rede, auf einem langsamen Macbook Air ...:

$ python -mtimeit -s"a='google';b='google'" 'a==b' 
10000000 loops, best of 3: 0.132 usec per loop 
$ python -mtimeit -s"a='google';b='google'" 'a is b' 
10000000 loops, best of 3: 0.107 usec per loop 
$ python -mtimeit -s"a='goo.gle';b='goo.gle'" 'a==b' 
10000000 loops, best of 3: 0.132 usec per loop 
$ python -mtimeit -s"a='google';b='google'" 'a is b' 
10000000 loops, best of 3: 0.106 usec per loop 
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a is b' 
10000000 loops, best of 3: 0.0966 usec per loop 
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a == b' 
10000000 loops, best of 3: 0.126 usec per loop 

... ein paar Dutzend Nanosekunden entweder übrigens, höchstens. Also, im Wert von sogar Denken über nur in den extremsten "optimieren Sie die [Kraftausdruck gelöscht] aus diesem [Kraftausdruck] Performance-Engpass" Situationen! -)

+8

Sieht so aus, als würde ich mich mißbrauchen ** ist ** operator – YOU

+1

@S.Mark, möglicherweise, aber nicht unbedingt - siehe meine Bearbeitung über das Praktikum. Normalerweise sollte 'is' nur auf Mutables wie Listen und Singletons wie' None' angewendet werden, aber wenn man sich dann internieren lässt (als wirklich mikroskopische Optimierung), kann man es auch dort verwenden (internieren macht auch '==' checks) ein kleines bisschen schneller, so dass Sie möglicherweise nicht "ist" einfügen müssen, selbst wenn Sie alle relevanten Strings ** religiös internieren! -). –

+2

Alex, Sie sagen "auf der obersten Ebene des Moduls (oder die interaktive Eingabeaufforderung)", aber ich glaube, was Sie beschreiben (und was das OP sah) * nur * passiert an der interaktiven Eingabeaufforderung - die oberste Ebene des Moduls ist noch in kompiliert ein einzelnes Codeobjekt, und alle Verweise auf dieselbe Konstante im gesamten Codeobjekt verwenden dieselbe Referenz. –

15

"ist" ist ein Identitätstest. Python hat ein Caching-Verhalten für kleine ganze Zahlen und (anscheinend) Zeichenfolgen. "ist" wird am besten für den Singletontest verwendet (z. B. None).

>>> x = "google" 
>>> x is "google" 
True 
>>> id(x) 
32553984L 
>>> id("google") 
32553984L 
>>> x = "google.com" 
>>> x is "google.com" 
False 
>>> id(x) 
32649320L 
>>> id("google.com") 
37787888L 
Verwandte Themen