Mengapa menetapkan ke daftar kosong (misalnya [] = "") tidak ada kesalahan?

110

Di python 3.4, saya mengetik

[] = "" 

dan berfungsi dengan baik, tidak ada Pengecualian yang dimunculkan. Padahal tentu saja []tidak sama dengan ""sesudahnya.

[] = ()

juga berfungsi dengan baik.

"" = []

menimbulkan pengecualian seperti yang diharapkan,

() = ""

menimbulkan pengecualian seperti yang diharapkan. Jadi apa yang terjadi?

Vardd
sumber

Jawaban:

132

Anda tidak membandingkan kesetaraan. Anda sedang menugaskan .

Python memungkinkan Anda untuk menetapkan ke beberapa target:

foo, bar = 1, 2

memberikan dua nilai ke foodan bar, masing-masing. Yang Anda butuhkan hanyalah urutan atau iterable di sisi kanan, dan daftar atau tupel nama di sebelah kiri.

Saat kamu melakukan:

[] = ""

Anda menetapkan urutan kosong (string kosong adalah urutan masih) ke daftar nama yang kosong.

Ini pada dasarnya sama dengan melakukan:

[foo, bar, baz] = "abc"

di mana Anda berakhir dengan foo = "a", bar = "b"dan baz = "c", tetapi dengan lebih sedikit karakter.

Namun, Anda tidak dapat menetapkan ke string, jadi ""di sisi kiri tugas tidak pernah berfungsi dan selalu ada kesalahan sintaks.

Lihat dokumentasi pernyataan Tugas :

Pernyataan tugas mengevaluasi daftar ekspresi (ingat bahwa ini dapat berupa ekspresi tunggal atau daftar yang dipisahkan koma, yang terakhir menghasilkan tupel) dan menetapkan objek tunggal yang dihasilkan ke setiap daftar target, dari kiri ke kanan.

dan

Penetapan objek ke daftar target, secara opsional diapit tanda kurung atau tanda kurung siku , secara rekursif didefinisikan sebagai berikut.

Penekanan milikku .

Python tidak memunculkan kesalahan sintaksis untuk daftar kosong sebenarnya adalah bug! Tata bahasa yang didokumentasikan secara resmi tidak memungkinkan untuk daftar target kosong, dan untuk yang kosong ()Anda mendapatkan kesalahan. Lihat bug 23275 ; itu dianggap bug yang tidak berbahaya:

Titik awalnya adalah menyadari bahwa ini telah ada sejak lama dan tidak berbahaya.

Juga lihat Mengapa valid untuk menetapkan ke daftar kosong tetapi tidak ke tupel kosong?

Martijn Pieters
sumber
36

Ini mengikuti aturan bagian Pernyataan tugas dari dokumentasi,

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

Jika target listadalah daftar target yang dipisahkan koma: Objek harus iterable dengan jumlah item yang sama karena ada target di daftar target, dan item ditetapkan, dari kiri ke kanan, ke target yang sesuai.

Objek harus berurutan dengan jumlah item yang sama dengan target di daftar target, dan item ditetapkan, dari kiri ke kanan, ke target yang sesuai.

Jadi, saat Anda berkata

[] = ""

"" adalah sebuah iterable (string python yang valid adalah sebuah iterable) dan itu sedang diurai di atas elemen-elemen dari list.

Sebagai contoh,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

Karena Anda memiliki string kosong, dan daftar kosong, tidak ada yang perlu dibuka. Jadi, tidak ada kesalahan.

Tapi, coba ini

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

Dalam [] = "1"kasus ini, Anda mencoba membongkar string di "1"atas daftar variabel kosong. Jadi itu mengeluh dengan "terlalu banyak nilai untuk dibongkar (diharapkan 0)".

Dengan cara yang sama, [a] = ""jika Anda memiliki string kosong, jadi tidak ada yang benar-benar perlu dibuka, tetapi Anda mengekstraknya ke satu variabel, yang, sekali lagi, tidak mungkin. Itulah mengapa ia mengeluh "membutuhkan lebih dari 0 nilai untuk dibuka".

Selain itu, seperti yang Anda perhatikan,

>>> [] = ()

juga tidak melempar kesalahan, karena ()merupakan tupel kosong.

>>> ()
()
>>> type(())
<class 'tuple'>

dan ketika itu dibongkar melalui daftar kosong, tidak ada yang perlu dibongkar. Jadi tidak ada kesalahan.


Tapi, saat Anda melakukannya

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

sebagai pesan kesalahan mengatakan, Anda mencoba untuk menetapkan ke string literal. Itu tidak mungkin. Itulah mengapa Anda mendapatkan kesalahan. Ini seperti mengatakan

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

Internal

Secara internal, operasi penugasan ini akan diterjemahkan ke UNPACK_SEQUENCEkode op,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

Di sini, karena string kosong, UNPACK_SEQUENCEbuka paket 0kali. Tetapi ketika Anda memiliki sesuatu seperti ini

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

urutannya 123dibongkar ke dalam tumpukan, dari kanan ke kiri. Jadi, bagian atas tumpukan adalah 1dan yang berikutnya adalah 2dan yang terakhir adalah 3. Kemudian menetapkan dari atas tumpukan ke variabel dari ekspresi sisi kiri satu per satu.


BTW, dengan Python, ini adalah bagaimana Anda dapat melakukan banyak tugas dalam ekspresi yang sama. Sebagai contoh,

a, b, c, d, e, f = u, v, w, x, y, z

ini berfungsi karena, nilai tangan kanan digunakan untuk membuat tupel dan kemudian akan diuraikan di atas nilai sisi kiri.

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

tetapi teknik pertukaran klasik a, b = b, amenggunakan rotasi elemen di bagian atas tumpukan. Jika Anda hanya memiliki dua atau tiga elemen maka mereka diperlakukan dengan special ROT_TWOdan ROT_THREEinstruksi alih-alih membangun tupel dan membongkar.

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
thefourtheye
sumber
Anda juga dapat menggunakan dis('[] = ""')tanpa menelepon compile().
Andrea Corbellini
Dapatkah Anda menjelaskan apa yang terjadi jika Anda menukar lebih dari tiga variabel / elemen, menggunakan metode di contoh terakhir Anda?
nanofarad
@hexafraction Ini akan membangun tupel baru dengan semua elemen di sisi kanan dan kemudian akan mengekstraknya di atas variabel di sisi kiri.
thefourtheye