Interning di stringhe Python


92

Sebbene questa domanda non abbia alcun reale utilizzo nella pratica, sono curioso di sapere come Python esegue l'internamento delle stringhe. Ho notato quanto segue.

>>> "string" is "string"
True

Questo è come mi aspettavo.

Puoi anche farlo.

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

E questo è abbastanza intelligente!

Ma non puoi farlo.

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

Perché Python non valuta s1+"g"e si rende conto che è la stessa cosa s2e lo punta allo stesso indirizzo? Cosa sta effettivamente succedendo in quell'ultimo blocco per riaverlo False?

Risposte:


95

Questo è specifico dell'implementazione, ma il tuo interprete probabilmente sta internando costanti in fase di compilazione ma non i risultati delle espressioni in fase di esecuzione.

In quanto segue utilizzo CPython 2.7.3.

Nel secondo esempio, l'espressione "strin"+"g"viene valutata in fase di compilazione e viene sostituita con "string". Ciò fa sì che i primi due esempi si comportino allo stesso modo.

Se esaminiamo i bytecode, vedremo che sono esattamente gli stessi:

  # s1 = "string"
  2           0 LOAD_CONST               1 ('string')
              3 STORE_FAST               0 (s1)

  # s2 = "strin" + "g"
  3           6 LOAD_CONST               4 ('string')
              9 STORE_FAST               1 (s2)

Il terzo esempio prevede una concatenazione run-time, il cui risultato non viene automaticamente internato:

  # s3a = "strin"
  # s3 = s3a + "g"
  4          12 LOAD_CONST               2 ('strin')
             15 STORE_FAST               2 (s3a)

  5          18 LOAD_FAST                2 (s3a)
             21 LOAD_CONST               3 ('g')
             24 BINARY_ADD          
             25 STORE_FAST               3 (s3)
             28 LOAD_CONST               0 (None)
             31 RETURN_VALUE        

Se dovessi manualmente intern()il risultato della terza espressione, otterrai lo stesso oggetto di prima:

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

22
E per la cronaca: ottimizzazione Peep-Hole di Python sarà pre-calcolare operazioni aritmetiche su costanti ( "string1" + "s2", 10 + 3*20, ecc) in fase di compilazione, ma limiti derivanti sequenze a soli 20 elementi (per evitare che [None] * 10**1000dal eccessivamente espandere la vostra bytecode). È questa ottimizzazione che è crollata "strin" + "g"in "string"; il risultato è inferiore a 20 caratteri.
Martijn Pieters

13
E per renderlo doppiamente chiaro: qui non si svolgono affatto tirocini. I letterali immutabili vengono invece archiviati come costanti con il bytecode. Internato non avvenire per i nomi utilizzati nel codice, ma non per i valori di stringa creati dal programma se non specificatamente internati dalla intern()funzione di.
Martijn Pieters

9
Per coloro che cercano di trovare la internfunzione in Python 3 - viene spostato su sys.intern
Timofey Chernousov

1

Caso 1

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

Caso 2

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

Ora, la tua domanda è perché l'id è lo stesso nel caso 1 e non nel caso 2.
Nel caso 1, hai assegnato una stringa letterale "123"a xe y.

Poiché le stringhe sono immutabili, ha senso che l'interprete memorizzi la stringa letterale solo una volta e punti tutte le variabili allo stesso oggetto.
Quindi vedi l'id come identico.

Nel caso 2, stai modificando xusando la concatenazione. Entrambi xe yhanno gli stessi valori, ma non la stessa identità.
Entrambi puntano a oggetti diversi in memoria. Quindi hanno diversi ide l' isoperatore è tornatoFalse


Come mai, dato che le stringhe sono immutabili, l'assegnazione di x + "3" (e la ricerca di un nuovo punto in cui memorizzare la stringa) non assegna allo stesso riferimento di y?
Nicecatch

Perché allora deve confrontare la nuova stringa con tutte le stringhe esistenti; un'operazione potenzialmente molto costosa. Potrebbe farlo in background dopo l'assegnazione suppongo, per ridurre la memoria, ma poi si finirebbe con un comportamento ancora più strano: id(x) != id(x)per esempio, perché la stringa è stata spostata durante il processo di valutazione.
DylanYoung

1
@AndreaConte perché la concatenazione di stringhe non fa il lavoro extra di cercare nel pool di tutte le stringhe utilizzate ogni volta che ne genera una nuova. D'altra parte, l'interprete "ottimizza" l'espressione x = "12" + "3"in x = "123"(concatenazione di due stringhe letterali in una singola espressione) in modo che l'assegnazione effettui la ricerca e trovi la stessa stringa "interna" di y = "123".
derenio

In realtà, non è che l'assegnazione esegue la ricerca piuttosto che ogni stringa letterale dal codice sorgente viene "interiorizzata" e quell'oggetto viene riutilizzato in tutti gli altri posti.
derenio
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.