Pembongkaran, pembongkaran diperpanjang dan pembongkaran diperpanjang bersarang

105

Perhatikan ungkapan berikut. Perhatikan bahwa beberapa ekspresi diulang untuk menampilkan "konteks".

(ini adalah daftar yang panjang)

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 cara menyimpulkan dengan benar hasil ekspresi seperti itu dengan tangan?

treecoder
sumber
28
Sejujurnya, sebagian besar jauh lebih rumit daripada yang Anda lihat dalam kode setiap hari. Pelajari dasar-dasar membongkar daftar / tupel dan Anda akan baik-baik saja.
Rafe Kettler
2
Perhatikan bahwa ini rekursif. Jadi, jika Anda memahami beberapa yang pertama, Anda dapat menangani semuanya. Coba ganti, misalnya, * (* a, b) dengan * x, cari tahu apa yang dibongkar x dan kemudian colokkan (* a, b) kembali ke x, dll.
Peteris
4
@greengit Saya menganggap diri saya memiliki pengetahuan tingkat lanjut tentang Python dan saya hanya tahu aturan umumnya :) Anda tidak perlu mengetahui setiap kasus sudut, Anda hanya kadang-kadang perlu menjalankan penerjemah dan menguji sesuatu.
Rafe Kettler
Wow, daftar yang bagus. Saya benar-benar tidak tahu tentang a, *b = 1, 2, 3jenis membongkar. Tapi ini Py3k kan?
Niklas R

Jawaban:

113

Saya minta maaf untuk panjangnya posting ini, tetapi saya memutuskan untuk memilih kelengkapan.

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

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

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

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

Selain itu, saat Anda melihat koma "polos", anggaplah ada tupel tingkat atas. Lakukan ini di sisi kiri dan kanan (yaitu untuk lvalues 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, kami mengonversi "XY"ke ('X', 'Y'), dan menutupi tanda koma dalam tanda kurung:

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

Korespondensi visual di sini memperjelas cara kerja tugas.

Inilah contoh yang salah:

(a,b), c = "XYZ"

Mengikuti aturan substitusi di atas, kami mendapatkan yang di bawah ini:

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

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

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

Menerapkan aturan di atas, kami mengerti

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

Tapi sekarang sudah jelas dari struktur yang 'this'tidak akan dibongkar, tapi ditugaskan langsung ke c. Jadi kami membatalkan pergantian.

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

Sekarang mari kita lihat apa yang terjadi saat kita membungkus ctupel:

(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. cbukan lagi variabel polos, tetapi variabel di dalam suatu urutan, sehingga urutan yang sesuai di sebelah kanan akan diuraikan (c,). Tetapi urutannya memiliki panjang yang berbeda, jadi ada kesalahan.

Sekarang untuk pembongkaran 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 termudah untuk menganalisis ini adalah bekerja dari ujung. 'X'ditugaskan ke adan 'Y'ditugaskan ke c. Nilai yang tersisa dalam urutan dimasukkan ke dalam daftar dan ditetapkan keb .

Nilai-nilai seperti (*a, b)dan (a, *b)hanyalah kasus khusus di atas. Anda tidak dapat memiliki dua *operator dalam satu urutan lvalue karena akan menjadi ambigu. Kemana nilai-nilai itu masuk ke dalam sesuatu seperti ini (a, *b, *c, d)- dalam batau c? Saya akan mempertimbangkan kasus bersarang sebentar lagi.

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

Di sini kesalahannya cukup jelas. Target ( *a) harus dalam tupel.

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

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

(*a,) = (1, 2)

Karena tidak ada variabel selain *a, *aslurps 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 berurutan, dan *amembutuhkan sesuatu untuk dibuka. Jadi kami menyusunnya secara berurutan

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

Yang setara dengan

(*a,) = (1,)

Akhirnya, ini adalah titik kebingungan yang umum: (1)sama dengan 1- Anda memerlukan 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 tupel tingkat atas ditugaskan, dan nilai yang tersisa di tupel tingkat atas ( 2dan 3) ditetapkan ke c- seperti yang 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 menimbulkan kesalahan. Baris kedua konyol tapi inilah mengapa berhasil:

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

Seperti yang telah dijelaskan sebelumnya, kami bekerja dari ujung. 3ditetapkan ke c, dan kemudian nilai yang tersisa ditetapkan ke variabel dengan *sebelumnya, dalam hal ini (a, b),. Jadi itu setara dengan (a, b) = (1, 2), yang kebetulan berhasil karena ada jumlah elemen yang tepat. Saya tidak dapat 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, 's'ditugaskan ke c, dan ('t', 'h', 'i')ditugaskan ke (a, *b). Bekerja lagi dari akhir, 't'ditugaskan ke a, dan ('h', 'i')ditugaskan ke b sebagai daftar. Ini adalah contoh konyol lain yang seharusnya tidak pernah muncul dalam kode kerja.

pengirim
sumber
24
Karena OP memberikan contoh daftar yang panjang, maka sepantasnya Anda memberikan daftar penjelasan yang panjang.
John Y
7

Saya menemukan tupel Python 2 membongkar cukup mudah. Setiap nama di sebelah kiri sesuai dengan seluruh urutan atau satu item dalam urutan di sebelah kanan. Jika nama sesuai dengan satu item dalam urutan apa pun, maka harus ada cukup nama untuk mencakup semua item.

Pembongkaran yang lama, bagaimanapun, tentu bisa membingungkan, karena sangat kuat. Kenyataannya adalah Anda tidak boleh melakukan 10 atau lebih contoh valid terakhir yang Anda berikan - jika datanya terstruktur, itu harus dalam dictcontoh kelas, bukan formulir tidak terstruktur seperti daftar.

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

Hanya karena Anda dapat menulis ekspresi kompleks yang sewenang-wenang, bukan berarti Anda harus melakukannya. Anda bisa menulis kode seperti map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))tetapi Anda tidak .

agf
sumber
Catatan: Saya telah menulis kode seperti itu, kecuali beberapa level yang lebih kompleks. Itu dimaksudkan hanya sebagai latihan, dan dilakukan dengan pengetahuan penuh bahwa setelah tiga bulan itu akan menjadi tidak berarti bagi saya, dan tidak akan pernah dapat dipahami oleh orang lain. Jika saya ingat benar, itu menerapkan titik dalam uji poligon, melakukan beberapa transformasi koordinat, dan membuat beberapa SVG, HTML, dan JavaScript.
agf
3

Saya pikir kode Anda mungkin menyesatkan, gunakan bentuk lain untuk mengekspresikannya.

Ini seperti menggunakan tanda kurung ekstra dalam ekspresi untuk menghindari pertanyaan tentang prioritas operator. Itu selalu merupakan investasi yang bagus untuk membuat kode Anda dapat dibaca.

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

Michał Šrajer
sumber