Mở gói, giải nén mở rộng và giải nén mở rộng lồng nhau


104

Hãy xem xét các biểu thức sau đây. Lưu ý rằng một số biểu thức được lặp lại để trình bày "ngữ cảnh".

(đây là một danh sách dài)

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

Làm thế nào để suy ra một cách chính xác kết quả của các biểu thức đó bằng tay?


28
Thành thật mà nói, hầu hết chúng phức tạp hơn nhiều so với những gì bạn thấy trong mã hàng ngày. Tìm hiểu những điều cơ bản về giải nén danh sách / bộ giá trị và bạn sẽ ổn.
Rafe Kettler

2
Lưu ý rằng đây là đệ quy. Vì vậy, nếu bạn gạch dưới một số đầu tiên, bạn có thể xử lý mọi thứ. Hãy thử thay thế, ví dụ: * (* a, b) bằng * x, tìm ra x giải nén những gì và sau đó cắm (* a, b) vào lại cho x, v.v.
Peteris

4
@greengit Tôi tự cho mình là người có kiến ​​thức nâng cao về Python và tôi chỉ biết các quy tắc chung :) Bạn không cần phải biết mọi trường hợp góc cạnh, bạn chỉ cần thỉnh thoảng khởi động một trình thông dịch và kiểm tra một cái gì đó.
Rafe Kettler

Wow danh sách tuyệt vời. Tôi thực sự không biết về a, *b = 1, 2, 3kiểu giải nén. Nhưng đây là Py3k phải không?
Niklas R

Câu trả lời:


113

Tôi xin lỗi vì độ dài của bài đăng này, nhưng tôi quyết định chọn sự hoàn chỉnh.

Khi bạn biết một vài quy tắc cơ bản, không khó để khái quát chúng. Tôi sẽ cố gắng giải thích bằng một vài ví dụ. Vì bạn đang nói về việc đánh giá những thứ này "bằng tay", tôi sẽ đề xuất một số quy tắc thay thế đơn giản. Về cơ bản, bạn có thể thấy dễ hiểu một biểu thức hơn nếu tất cả các tệp lặp được định dạng theo cùng một cách.

Chỉ với mục đích giải nén, các thay thế sau đây hợp lệ ở phía bên phải của =(tức là đối với các giá trị ):

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

Nếu bạn thấy rằng một giá trị không được giải nén, thì bạn sẽ hoàn tác việc thay thế. (Xem bên dưới để giải thích thêm.)

Ngoài ra, khi bạn nhìn thấy dấu phẩy "trần trụi", hãy giả sử có một bộ giá trị cấp cao nhất. Làm điều này trên cả hai bên trái và bên phải (tức là cho lvaluesrvalues ):

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

Với những quy tắc đơn giản đó, đây là một số ví dụ:

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

Áp dụng các quy tắc trên, chúng tôi chuyển đổi "XY"thành ('X', 'Y')và che dấu phẩy thường trong parens:

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

Sự tương ứng trực quan ở đây làm cho nó khá rõ ràng về cách thức hoạt động của nhiệm vụ.

Đây là một ví dụ sai lầm:

(a,b), c = "XYZ"

Theo các quy tắc thay thế ở trên, chúng tôi nhận được như sau:

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

Điều này rõ ràng là sai lầm; các cấu trúc lồng nhau không khớp với nhau. Bây giờ chúng ta hãy xem cách nó hoạt động cho một ví dụ phức tạp hơn một chút:

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

Áp dụng các quy tắc trên, chúng tôi nhận được

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

Nhưng bây giờ rõ ràng là cấu trúc 'this'sẽ không được giải nén mà được gán trực tiếp cho c. Vì vậy, chúng tôi hoàn tác thay thế.

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

Bây giờ chúng ta hãy xem điều gì sẽ xảy ra khi chúng ta quấn ctrong một bộ tuple:

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

Trở thành

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

Một lần nữa, lỗi là hiển nhiên. ckhông còn là một biến rỗng nữa, mà là một biến bên trong một chuỗi, và do đó, chuỗi tương ứng ở bên phải được giải nén vào (c,). Nhưng các trình tự có độ dài khác nhau nên sẽ xảy ra lỗi.

Bây giờ để giải nén mở rộng bằng cách sử dụng *nhà điều hành. Điều này phức tạp hơn một chút, nhưng nó vẫn khá đơn giản. Một biến đứng trước *sẽ trở thành một danh sách, chứa bất kỳ mục nào từ chuỗi tương ứng mà không được gán cho tên biến. Bắt đầu với một ví dụ khá đơn giản:

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

Điều này trở thành

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

Cách đơn giản nhất để phân tích điều này là làm việc từ đầu. 'X'được giao cho a'Y'được giao cho c. Các giá trị còn lại trong chuỗi được đưa vào danh sách và được gán cho b.

Các giá trị như (*a, b)(a, *b)chỉ là trường hợp đặc biệt của những điều trên. Bạn không thể có hai *toán tử bên trong một chuỗi giá trị vì nó sẽ không rõ ràng. Các giá trị sẽ đi đâu trong một cái gì đó như thế này (a, *b, *c, d)- trong bhoặc c? Tôi sẽ xem xét trường hợp lồng nhau trong giây lát.

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

Ở đây lỗi khá dễ hiểu. Đích ( *a) phải nằm trong một bộ giá trị.

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

Điều này hoạt động vì có một dấu phẩy thường. Áp dụng các quy tắc ...

(*a,) = (1, 2)

Vì không có biến nào khác hơn *a, *anên bỏ qua tất cả các giá trị trong chuỗi giá trị. Điều gì sẽ xảy ra nếu bạn thay thế (1, 2)bằng một giá trị duy nhất?

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

trở thành

(*a,) = 1

Một lần nữa, lỗi ở đây là tự giải thích. Bạn không thể giải nén thứ gì đó không phải là một chuỗi và *acần một thứ gì đó để giải nén. Vì vậy, chúng tôi đặt nó theo một trình tự

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

Hóa trị kép với

(*a,) = (1,)

Cuối cùng, đây là một điểm nhầm lẫn phổ biến: (1)cũng giống như 1- bạn cần dấu phẩy để phân biệt một bộ giá trị với một câu lệnh số học.

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

Bây giờ để làm tổ. Thực ra ví dụ này không có trong phần "NESTED" của bạn; Có lẽ bạn đã không nhận ra nó được lồng vào nhau?

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

Trở thành

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

Giá trị đầu tiên trong bộ tuple cấp cao nhất được gán và các giá trị còn lại trong bộ tuple cấp cao nhất ( 23) được gán choc - giống như chúng ta mong đợi.

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

Tôi đã giải thích ở trên lý do tại sao dòng đầu tiên gặp lỗi. Dòng thứ hai là ngớ ngẩn nhưng đây là lý do tại sao nó hoạt động:

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

Như đã giải thích trước đây, chúng tôi làm việc từ đầu. 3được gán cho c, và sau đó các giá trị còn lại được gán cho biến *đứng trước nó, trong trường hợp này (a, b),. Vì vậy, điều đó tương đương với (a, b) = (1, 2), điều này xảy ra hoạt động vì có đúng số phần tử. Tôi không thể nghĩ ra bất kỳ lý do nào mà điều này lại xuất hiện trong mã làm việc. Tương tự,

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

trở thành

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

Làm việc từ đầu, 's'được giao cho c, và ('t', 'h', 'i')được giao cho (a, *b). Làm việc lại từ đầu, 't'được giao cho a, và ('h', 'i')được giao cho b dưới dạng danh sách. Đây là một ví dụ ngớ ngẩn khác không bao giờ nên xuất hiện trong mã làm việc.


24
Vì OP đã đưa ra một danh sách dài các ví dụ, bạn chỉ cần đưa ra một danh sách dài các lời giải thích.
John Y

7

Tôi thấy việc giải nén bộ dữ liệu Python 2 khá đơn giản. Mỗi tên ở bên trái tương ứng với toàn bộ chuỗi hoặc một mục đơn lẻ trong chuỗi ở bên phải. Nếu các tên tương ứng với các mục đơn của bất kỳ trình tự nào, thì phải có đủ tên để bao hàm tất cả các mục.

Tuy nhiên, việc giải nén mở rộng chắc chắn có thể gây nhầm lẫn vì nó rất mạnh. Thực tế là bạn không bao giờ nên làm 10 ví dụ hợp lệ cuối cùng hoặc nhiều hơn mà bạn đã đưa ra - nếu dữ liệu có cấu trúc, nó phải ởdict hoặc một cá thể lớp, không phải các dạng không có cấu trúc như danh sách.

Rõ ràng, cú pháp mới có thể bị lạm dụng. Câu trả lời cho câu hỏi của bạn là bạn không nên cần phải đọc các biểu thức như vậy - chúng thực hành không tốt và tôi nghi ngờ chúng sẽ được sử dụng.

Chỉ vì bạn có thể viết các biểu thức phức tạp tùy ý không có nghĩa là bạn nên làm như vậy. Bạn có thể viết mã như thế map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))nhưng bạn không .


Lưu ý: Tôi đã viết mã như vậy, ngoại trừ một số cấp độ phức tạp hơn. Nó chỉ được dự định như một bài tập, và được thực hiện với đầy đủ kiến ​​thức rằng sau ba tháng nó sẽ trở nên vô nghĩa đối với tôi và sẽ không bao giờ có thể hiểu được đối với bất kỳ ai khác. Nếu tôi nhớ đúng, nó đã triển khai điểm trong kiểm tra đa giác, thực hiện một số chuyển đổi tọa độ và tạo một số SVG, HTML và JavaScript.
agf

3

Tôi nghĩ rằng mã của bạn có thể gây hiểu lầm, hãy sử dụng biểu mẫu khác để diễn đạt.

Nó giống như việc sử dụng thêm dấu ngoặc trong biểu thức để tránh các câu hỏi về mức độ ưu tiên của toán tử. Tôi luôn luôn là một khoản đầu tư tốt để làm cho mã của bạn có thể đọc được.

Tôi chỉ thích sử dụng giải nén cho các tác vụ đơn giản như hoán đổi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.