Python string stage

Hoewel deze vraag in de praktijk geen enkel nut heeft, ben ik benieuwd hoe Python string-interning doet. Ik heb het volgende opgemerkt.

>>> "string" is "string"
True

Dit is zoals ik had verwacht.

U kunt dit ook doen.

>>> "strin"+"g" is "string"
True

En dat is best slim!

Maar u kunt dit niet doen.

>>> s1 = "strin"
>>> s2 = "string"
>>> s1+"g" is s2
False

Waarom zou Python s1+"g"niet evalueren en beseffen dat het hetzelfde is als s2en het naar hetzelfde adres verwijzen? Wat gebeurt er eigenlijk in dat laatste blok om het Falsete laten retourneren?


Antwoord 1, autoriteit 100%

Dit is implementatiespecifiek, maar uw interpreter gebruikt waarschijnlijk compile-time-constanten, maar niet de resultaten van runtime-expressies.

In wat volgt wordt CPython 3.9.0+ gebruikt.

In het tweede voorbeeld wordt de uitdrukking "strin"+"g"geëvalueerd tijdens het compileren en vervangen door "string". Hierdoor gedragen de eerste twee voorbeelden zich hetzelfde.

Als we de bytecodes onderzoeken, zien we dat ze precies hetzelfde zijn:

 # s1 = "string"
  1           0 LOAD_CONST               0 ('string')
              2 STORE_NAME               0 (s1)
  # s2 = "strin" + "g"
  2           4 LOAD_CONST               0 ('string')
              6 STORE_NAME               1 (s2)

Deze bytecode is verkregen met (waarmee na het bovenstaande nog een paar regels worden afgedrukt):

import dis
source = 's1 = "string"\ns2 = "strin" + "g"'
code = compile(source, '', 'exec')
print(dis.dis(code))

Het derde voorbeeld betreft een runtime-aaneenschakeling, waarvan het resultaat niet automatisch wordt geïnterneerd:

 # s3a = "strin"
  3           8 LOAD_CONST               1 ('strin')
             10 STORE_NAME               2 (s3a)
  # s3 = s3a + "g"
  4          12 LOAD_NAME                2 (s3a)
             14 LOAD_CONST               2 ('g')
             16 BINARY_ADD
             18 STORE_NAME               3 (s3)
             20 LOAD_CONST               3 (None)
             22 RETURN_VALUE

Deze bytecode is verkregen met (die nog een paar regels voor het bovenstaande afdrukt, en die regels zijn precies zoals in het eerste blok met bytecodes hierboven):

import dis
source = (
    's1 = "string"\n'
    's2 = "strin" + "g"\n'
    's3a = "strin"\n'
    's3 = s3a + "g"')
code = compile(source, '', 'exec')
print(dis.dis(code))

Als je handmatig sys.intern()het resultaat van de derde expressie, je krijgt hetzelfde object als voorheen:

>>> import sys
>>> s3a = "strin"
>>> s3 = s3a + "g"
>>> s3 is "string"
False
>>> sys.intern(s3) is "string"
True

Python 3.9 drukt ook een waarschuwing af voor de laatste twee bovenstaande uitspraken:

SyntaxWarning: “is” met een letterlijke. Bedoelde je “==”?


Antwoord 2, autoriteit 3%

Geval 1

>>> x = "123"  
>>> y = "123"  
>>> x == y  
True  
>>> x is y  
True  
>>> id(x)  
50986112  
>>> id(y)  
50986112  

Geval 2

>>> x = "12"
>>> y = "123"
>>> x = x + "3"
>>> x is y
False
>>> x == y
True

Nu, uw vraag is waarom de ID hetzelfde is in het geval 1 en niet in het geval 2.
In geval 1 heeft u een string-letterlijke "123"toegewezen aan xen y.

Aangezien string onveranderlijk is, is het logisch voor de tolk om de letterlijke string slechts één keer op te slaan en wijzen alle variabelen naar hetzelfde object.
Vandaar dat u de ID als identiek ziet.

In geval 2, u wijzigt xmet behulp van aaneenschakeling. Zowel xen yheeft dezelfde waarden, maar niet dezelfde identiteit.
Beide punten op verschillende objecten in het geheugen. Daarom hebben ze verschillende iden isoperator geretourneerd False

Other episodes