Pertanyaan Membuka kemasan, membongkar diperpanjang, dan mengulur diperpanjang bersarang


Pertimbangkan ungkapan-ungkapan ini ... Harap bersabar ... ini adalah daftar yang panjang ...

(Catatan: beberapa ekspresi diulang - ini hanya untuk menyajikan "konteks")

a, b = 1, 2                          # simple sequence assignment
a, b = ['green', 'blue']             # list asqignment
a, b = 'XY'                          # string assignment
a, b = range(1,5,2)                  # any iterable will do


                                     # nested sequence assignment

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z' 

(a,b), c = "XYZ"                     # ERROR -- too many values to unpack
(a,b), c = "XY"                      # ERROR -- need more than 1 value to unpack

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'
(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack


                                     # extended sequence unpacking

a, *b = 1,2,3,4,5                    # a = 1, b = [2,3,4,5]
*a, b = 1,2,3,4,5                    # a = [1,2,3,4], b = 5
a, *b, c = 1,2,3,4,5                 # a = 1, b = [2,3,4], c = 5

a, *b = 'X'                          # a = 'X', b = []
*a, b = 'X'                          # a = [], b = 'X'
a, *b, c = "XY"                      # a = 'X', b = [], c = 'Y'
a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

a, b, *c = 1,2,3                     # a = 1, b = 2, c = [3]
a, b, c, *d = 1,2,3                  # a = 1, b = 2, c = 3, d = []

a, *b, c, *d = 1,2,3,4,5             # ERROR -- two starred expressions in assignment

(a,b), c = [1,2],'this'              # a = '1', b = '2', c = 'this'
(a,b), *c = [1,2],'this'             # a = '1', b = '2', c = ['this']

(a,b), c, *d = [1,2],'this'          # a = '1', b = '2', c = 'this', d = []
(a,b), *c, d = [1,2],'this'          # a = '1', b = '2', c = [], d = 'this'

(a,b), (c, *d) = [1,2],'this'        # a = '1', b = '2', c = 't', d = ['h', 'i', 's']

*a = 1                               # ERROR -- target must be in a list or tuple
*a = (1,2)                           # ERROR -- target must be in a list or tuple
*a, = (1,2)                          # a = [1,2]
*a, = 1                              # ERROR -- 'int' object is not iterable
*a, = [1]                            # a = [1]
*a = [1]                             # ERROR -- target must be in a list or tuple
*a, = (1,)                           # a = [1]
*a, = (1)                            # ERROR -- 'int' object is not iterable

*a, b = [1]                          # a = [], b = 1
*a, b = (1,)                         # a = [], b = 1

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
(a,b), *c = 1,2,3                    # ERROR - 'int' object is not iterable
(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]


                                     # extended sequence unpacking -- NESTED

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

*(a,b) = 1,2                         # ERROR -- target must be in a list or tuple
*(a,b), = 1,2                        # a = 1, b = 2

*(a,b) = 'XY'                        # ERROR -- target must be in a list or tuple
*(a,b), = 'XY'                       # a = 'X', b = 'Y'

*(a, b) = 'this'                     # ERROR -- target must be in a list or tuple
*(a, b), = 'this'                    # ERROR -- too many values to unpack
*(a, *b), = 'this'                   # a = 't', b = ['h', 'i', 's']

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

*(a,*b), = 1,2,3,3,4,5,6,7           # a = 1, b = [2, 3, 3, 4, 5, 6, 7]

*(a,*b), *c = 1,2,3,3,4,5,6,7        # ERROR -- two starred expressions in assignment
*(a,*b), (*c,) = 1,2,3,3,4,5,6,7     # ERROR -- 'int' object is not iterable
*(a,*b), c = 1,2,3,3,4,5,6,7         # a = 1, b = [2, 3, 3, 4, 5, 6], c = 7
*(a,*b), (*c,) = 1,2,3,4,5,'XY'      # a = 1, b = [2, 3, 4, 5], c = ['X', 'Y']

*(a,*b), c, d = 1,2,3,3,4,5,6,7      # a = 1, b = [2, 3, 3, 4, 5], c = 6, d = 7
*(a,*b), (c, d) = 1,2,3,3,4,5,6,7    # ERROR -- 'int' object is not iterable
*(a,*b), (*c, d) = 1,2,3,3,4,5,6,7   # ERROR -- 'int' object is not iterable
*(a,*b), *(c, d) = 1,2,3,3,4,5,6,7   # ERROR -- two starred expressions in assignment


*(a,b), c = 'XY', 3                  # ERROR -- need more than 1 value to unpack
*(*a,b), c = 'XY', 3                 # a = [], b = 'XY', c = 3
(a,b), c = 'XY', 3                   # a = 'X', b = 'Y', c = 3

*(a,b), c = 'XY', 3, 4               # a = 'XY', b = 3, c = 4
*(*a,b), c = 'XY', 3, 4              # a = ['XY'], b = 3, c = 4
(a,b), c = 'XY', 3, 4                # ERROR -- too many values to unpack

Bagaimana Anda memahami kerumitan dan kebingungan semacam itu. Bagaimana seseorang bisa selalu KANAN saat menghitung hasil ekspresi tersebut dengan tangan. Atau, ketika membaca kode orang lain, haruskah saya mengabaikannya dan tidak pernah mencoba memahami apa yang sebenarnya dilakukan ekspresi itu?


75
2017-08-06 14:59


asal


Jawaban:


Saya minta maaf untuk panjang posting ini, tapi saya memutuskan untuk memilih kelengkapan.

Setelah Anda mengetahui beberapa aturan dasar, tidaklah sulit untuk menyamaratakannya. Saya akan melakukan yang terbaik untuk menjelaskan dengan beberapa contoh. Karena Anda sedang berbicara tentang mengevaluasi ini "dengan tangan," saya akan menyarankan beberapa aturan substitusi sederhana. Pada dasarnya, Anda mungkin lebih mudah memahami ekspresi jika semua iterformenya diformat dengan cara yang sama.

Untuk tujuan membongkar saja, substitusi berikut ini berlaku di sisi kanan = (yaitu untuk rvalues):

'XY' -> ('X', 'Y')
['X', 'Y'] -> ('X', 'Y')

Jika Anda menemukan bahwa nilai tidak terbongkar, maka Anda akan membatalkan substitusi. (Lihat di bawah untuk penjelasan lebih lanjut.)

Juga, ketika Anda melihat koma "telanjang", berpura-pura ada tuple tingkat atas. Lakukan ini di sisi kiri dan kanan (yaitu untuk nilai-nilai dan rvalues):

'X', 'Y' -> ('X', 'Y')
a, b -> (a, b)

Dengan mengingat aturan sederhana tersebut, berikut beberapa contohnya:

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z'

Menerapkan aturan di atas, kita mengkonversi "XY" untuk ('X', 'Y'), dan menutupi koma telanjang di parens:

((a, b), c) = (('X', 'Y'), 'Z')

Korespondensi visual di sini membuatnya cukup jelas bagaimana penugasan itu bekerja.

Berikut ini contoh yang salah:

(a,b), c = "XYZ"

Mengikuti aturan substitusi di atas, kita mendapatkan yang berikut:

((a, b), c) = ('X', 'Y', 'Z')

Ini jelas keliru; struktur bersarang tidak cocok. Sekarang mari kita lihat cara kerjanya untuk contoh yang sedikit lebih rumit:

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'

Menerapkan aturan di atas, kita dapatkan

((a, b), c) = ((1, 2), ('t', 'h', 'i', 's'))

Tapi sekarang sudah jelas dari struktur itu 'this' tidak akan dibongkar, tetapi ditugaskan langsung ke c. Jadi kami membatalkan substitusi.

((a, b), c) = ((1, 2), 'this')

Sekarang mari kita lihat apa yang terjadi ketika kita membungkus c dalam tupel:

(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack

Menjadi

((a, b), (c,)) = ((1, 2), ('t', 'h', 'i', 's'))

Sekali lagi, kesalahannya jelas. c bukan lagi variabel telanjang, tetapi variabel di dalam urutan, sehingga urutan yang sesuai di sebelah kanan dibuka (c,). Tetapi urutan memiliki panjang yang berbeda, jadi ada kesalahan.

Sekarang untuk membongkar diperpanjang menggunakan * operator. Ini sedikit lebih rumit, tetapi masih cukup mudah. Variabel yang diawali dengan * menjadi daftar, yang berisi item apa pun dari urutan terkait yang tidak ditetapkan ke nama variabel. Dimulai dengan contoh yang cukup sederhana:

a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

Ini menjadi

(a, *b, c) = ('X', '.', '.', '.', 'Y')

Cara paling sederhana untuk menganalisis ini adalah bekerja dari ujungnya. 'X' ditugaskan untuk a dan 'Y' ditugaskan untuk c. Nilai-nilai yang tersisa dalam urutan dimasukkan ke dalam daftar dan ditugaskan untuk b.

Nilai seperti (*a, b) dan (a, *b) hanya kasus-kasus khusus di atas. Anda tidak dapat memiliki dua *operator dalam satu urutan nilai karena akan menjadi ambigu. Di mana nilai-nilai masuk seperti ini (a, *b, *c, d) -- di b atau c? Saya akan mempertimbangkan kasus bersarang ini sebentar lagi.

*a = 1                               # ERROR -- target must be in a list or tuple

Di sini kesalahannya cukup jelas. Sasaran (*a) harus dalam tuple.

*a, = (1,2)                          # a = [1,2]

Ini berfungsi karena ada koma telanjang. Menerapkan aturan ...

(*a,) = (1, 2)

Karena tidak ada variabel lain selain *a, *a slurps semua nilai dalam urutan rvalue. Bagaimana jika Anda mengganti (1, 2) dengan satu nilai?

*a, = 1                              # ERROR -- 'int' object is not iterable

menjadi

(*a,) = 1

Sekali lagi, kesalahan di sini sudah cukup jelas. Anda tidak dapat membongkar sesuatu yang bukan urutan, dan *a butuh sesuatu untuk dibongkar. Jadi kami menempatkannya secara berurutan

*a, = [1]                            # a = [1]

Yang ekuivalen dengan

(*a,) = (1,)

Akhirnya, ini adalah titik kebingungan yang umum: (1) sama dengan 1 - Anda perlu koma untuk membedakan tupel dari pernyataan aritmatika.

*a, = (1)                            # ERROR -- 'int' object is not 

Sekarang untuk bersarang. Sebenarnya contoh ini tidak ada di bagian "NESTED" Anda; mungkin Anda tidak menyadari itu bersarang?

(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]

Menjadi

((a, b), *c) = (('X', 'Y'), 2, 3)

Nilai pertama di tuple tingkat atas ditetapkan, dan nilai yang tersisa di tupel tingkat atas (2 dan 3) ditugaskan untuk c - seperti yang seharusnya kita harapkan.

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

Saya sudah menjelaskan di atas mengapa baris pertama melempar kesalahan. Baris kedua konyol tapi inilah mengapa itu berhasil:

(*(a, b), c) = (1, 2, 3)

Sebagaimana dijelaskan sebelumnya, kami bekerja dari ujungnya. 3 ditugaskan untuk c, dan kemudian nilai-nilai yang tersisa ditugaskan ke variabel dengan * sebelumnya, dalam hal ini, (a, b). Jadi itu setara dengan (a, b) = (1, 2), yang berfungsi karena ada jumlah elemen yang tepat. Saya tidak bisa memikirkan alasan apa pun ini akan muncul dalam kode kerja. Demikian pula,

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

menjadi

(*(a, *b), c) = ('t', 'h', 'i', 's')

Bekerja dari ujung-ujungnya, 's' ditugaskan untuk c, dan ('t', 'h', 'i') ditugaskan untuk (a, *b). Bekerja lagi dari ujung-ujungnya, 't' ditugaskan untuk a, dan ('h', 'i') ditugaskan ke b sebagai daftar. Ini adalah contoh konyol lain yang seharusnya tidak pernah muncul dalam kode kerja.


87
2017-08-06 17:30



Saya menemukan tuple Python 2 membongkar cukup mudah. Setiap nama di sebelah kiri sesuai dengan urutan keseluruhan atau satu item dalam urutan di sebelah kanan. Jika nama-nama sesuai dengan satu item dari setiap urutan, maka harus ada cukup nama untuk mencakup semua item.

Diperpanjang membongkar, bagaimanapun, pasti bisa membingungkan, karena itu sangat kuat. Kenyataannya adalah Anda tidak boleh melakukan 10 atau lebih contoh valid terakhir yang Anda berikan - jika data terstruktur, itu harus dalam dict atau instance kelas, bukan formulir tidak terstruktur seperti daftar.

Jelas, sintaks baru dapat disalahgunakan. Jawaban atas pertanyaan Anda adalah bahwa Anda seharusnya tidak harus membaca ekspresi seperti itu - mereka praktik yang buruk dan saya ragu mereka akan digunakan.

Hanya karena Anda dapat menulis ekspresi yang semrawut rumit, bukan berarti Anda harus. Anda bisa menulis kode seperti map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables)) tapi kamu tidak.


6
2017-08-06 15:13



Saya rasa kode Anda mungkin menyesatkan penggunaan formulir lain untuk mengekspresikannya.

Ini seperti menggunakan kurung ekstra dalam ekspresi untuk menghindari pertanyaan tentang operator yang diutamakan. Saya tidak selalu merupakan investasi yang bagus untuk membuat kode Anda terbaca.

Saya lebih suka menggunakan membongkar hanya untuk tugas-tugas sederhana seperti swap.


2
2017-08-06 15:08