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 s2
en het naar hetzelfde adres verwijzen? Wat gebeurt er eigenlijk in dat laatste blok om het False
te 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 x
en 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 x
met behulp van aaneenschakeling. Zowel x
en y
heeft dezelfde waarden, maar niet dezelfde identiteit.
Beide punten op verschillende objecten in het geheugen. Daarom hebben ze verschillende id
en is
operator geretourneerd False