Pemagangan string Python

92

Meskipun pertanyaan ini tidak memiliki kegunaan nyata dalam praktiknya, saya ingin tahu bagaimana Python melakukan interning string. Saya telah memperhatikan yang berikut ini.

>>> "string" is "string"
True

Ini seperti yang saya harapkan.

Anda juga bisa melakukan ini.

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

Dan itu sangat pintar!

Tapi Anda tidak bisa melakukan ini.

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

Mengapa Python tidak mengevaluasi s1+"g", dan menyadari bahwa itu sama s2dan mengarahkannya ke alamat yang sama? Apa yang sebenarnya terjadi di blok terakhir untuk mendapatkannya kembali False?

Ze'ev G
sumber

Jawaban:

95

Ini khusus untuk penerapan, tetapi penafsir Anda mungkin menginangi konstanta waktu kompilasi tetapi bukan hasil dari ekspresi waktu proses.

Berikut ini saya menggunakan CPython 2.7.3.

Dalam contoh kedua, ekspresi "strin"+"g"dievaluasi pada waktu kompilasi, dan diganti dengan "string". Ini membuat dua contoh pertama berperilaku sama.

Jika kita memeriksa bytecode, kita akan melihat bahwa keduanya persis sama:

  # 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)

Contoh ketiga melibatkan rangkaian run-time, yang hasilnya tidak otomatis disimpan:

  # 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        

Jika Anda secara manual intern()mendapatkan hasil dari ekspresi ketiga, Anda akan mendapatkan objek yang sama seperti sebelumnya:

>>> s3a = "strin"
>>> s3 = s3a + "g"
>>> s3 is "string"
False
>>> intern(s3) is "string"
True
NPE
sumber
22
Dan untuk catatan: optimasi mengintip-lubang Python akan pra-menghitung operasi aritmatika pada konstanta ( "string1" + "s2", 10 + 3*20, dll) pada waktu kompilasi, tetapi batas dihasilkan urutan hanya 20 elemen (untuk mencegah [None] * 10**1000dari terlalu memperluas bytecode Anda). Pengoptimalan inilah yang runtuh "strin" + "g"menjadi "string"; hasilnya lebih pendek dari 20 karakter.
Martijn Pieters
13
Dan untuk membuatnya sangat jelas: tidak ada interniran yang terjadi di sini sama sekali. Literal yang tidak dapat diubah disimpan sebagai konstanta dengan bytecode. Magang yang berlangsung untuk nama yang digunakan dalam kode, tetapi tidak untuk nilai string yang dibuat oleh program kecuali secara khusus diinternir oleh intern()fungsi.
Martijn Pieters
9
Bagi mereka, yang mencoba menemukan internfungsi di Python 3 - itu dipindahkan ke sys.intern
Timofey Chernousov
1

Kasus 1

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

Kasus 2

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

Sekarang, pertanyaan Anda adalah mengapa id sama dalam kasus 1 dan bukan dalam kasus 2.
Dalam kasus 1, Anda telah menetapkan literal string "123"ke xdan y.

Karena string tidak dapat diubah, masuk akal bagi penerjemah untuk menyimpan string literal hanya sekali dan mengarahkan semua variabel ke objek yang sama.
Karenanya Anda melihat id sebagai identik.

Dalam kasus 2, Anda memodifikasi xmenggunakan penggabungan. Keduanya xdan ymemiliki nilai yang sama, tetapi tidak memiliki identitas yang sama.
Keduanya menunjuk ke objek yang berbeda dalam memori. Oleh karena itu mereka berbeda iddan isoperator kembaliFalse

cppcoder
sumber
Kenapa, karena string tidak dapat diubah, menetapkan x + "3" (dan mencari tempat baru untuk menyimpan string) tidak memberikan referensi yang sama dengan y?
nicecatch
Karena itu perlu membandingkan string baru dengan semua string yang ada; berpotensi menjadi operasi yang sangat mahal. Ini bisa melakukan ini di latar belakang setelah tugas Saya kira, untuk mengurangi memori, tetapi kemudian Anda akan berakhir dengan perilaku yang lebih aneh: id(x) != id(x)misalnya, karena string dipindahkan dalam proses evaluasi.
DylanYoung
1
@AndreaConte karena penggabungan string tidak melakukan pekerjaan tambahan untuk mencari ke kumpulan semua string yang digunakan setiap kali membuat yang baru. Di sisi lain, interpreter "mengoptimalkan" ekspresi x = "12" + "3"ke dalam x = "123"(penggabungan dua string literal dalam ekspresi tunggal) sehingga tugas benar-benar melakukan pencarian dan menemukan string "internal" yang sama seperti untuk y = "123".
derenio
Sebenarnya, ini bukan karena tugas melakukan pencarian daripada setiap string literal dari kode sumber yang "diinternalisasi" dan objek itu digunakan kembali di semua tempat lain.
derenio