Tại sao việc gán cho một danh sách trống (ví dụ: [] = “”) không phải là một lỗi?


110

Trong python 3.4, tôi đang nhập

[] = "" 

và nó hoạt động tốt, không có Ngoại lệ nào được đưa ra. Tuy nhiên sau []này không bằng "".

[] = ()

cũng hoạt động tốt.

"" = []

đưa ra một ngoại lệ như mong đợi,

() = ""

đưa ra một ngoại lệ như mong đợi mặc dù. Vì vậy những gì đang xảy ra?

Câu trả lời:


132

Bạn không so sánh để bình đẳng. Bạn đang chỉ định .

Python cho phép bạn gán cho nhiều mục tiêu:

foo, bar = 1, 2

gán hai giá trị tương ứng cho foobar. Tất cả những gì bạn cần là một chuỗi hoặc có thể lặp lại ở phía bên phải và một danh sách hoặc nhiều tên ở bên trái.

Khi bạn làm:

[] = ""

bạn đã gán một chuỗi trống (chuỗi trống là chuỗi vẫn còn) cho một danh sách tên trống.

Về cơ bản nó giống như làm:

[foo, bar, baz] = "abc"

nơi bạn kết thúc với foo = "a", bar = "b"baz = "c"nhưng có ít ký tự hơn.

Tuy nhiên, bạn không thể gán cho một chuỗi, vì vậy ""ở phía bên trái của phép gán không bao giờ hoạt động và luôn là lỗi cú pháp.

Xem tài liệu Báo cáo bài tập :

Một câu lệnh gán đánh giá danh sách biểu thức (hãy nhớ rằng đây có thể là một biểu thức đơn lẻ hoặc một danh sách được phân tách bằng dấu phẩy, câu lệnh sau mang lại một bộ giá trị) và gán một đối tượng kết quả duy nhất cho từng danh sách đích, từ trái sang phải.

Việc gán một đối tượng vào danh sách đích, tùy chọn được đặt trong dấu ngoặc đơn hoặc dấu ngoặc vuông , được định nghĩa đệ quy như sau.

Nhấn mạnh của tôi .

Việc Python không đưa ra lỗi cú pháp cho danh sách trống thực sự là một chút lỗi! Ngữ pháp được tài liệu chính thức không cho phép danh sách mục tiêu trống và đối với danh sách trống, ()bạn sẽ gặp lỗi. Xem lỗi 23275 ; nó được coi là một lỗi vô hại:

Điểm khởi đầu là nhận ra rằng điều này đã có từ rất lâu và vô hại.

Cũng xem Tại sao việc gán cho một danh sách trống nhưng không hợp lệ cho một bộ giá trị trống?


36

Nó tuân theo các quy tắc của phần câu lệnh Bài tập từ tài liệu,

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

Nếu target listlà một danh sách mục tiêu được phân tách bằng dấu phẩy: Đối tượng phải là một đối tượng có thể lặp lại với cùng số lượng các mục như có các mục tiêu trong danh sách mục tiêu và các mục được gán, từ trái sang phải, cho các mục tiêu tương ứng.

Đối tượng phải là một chuỗi có cùng số mục vì có các mục tiêu trong danh sách mục tiêu và các mục được gán, từ trái sang phải, cho các mục tiêu tương ứng.

Vì vậy, khi bạn nói

[] = ""

"" là một chuỗi có thể lặp lại (bất kỳ chuỗi python hợp lệ nào là có thể lặp lại) và nó đang được giải nén qua các phần tử của danh sách.

Ví dụ,

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

Vì bạn có một chuỗi trống và một danh sách trống, nên không có gì để giải nén. Vì vậy, không có lỗi.

Nhưng, hãy thử cái này

>>> [] = "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

Trong [] = "1"trường hợp này, bạn đang cố gắng giải nén chuỗi "1"trên một danh sách các biến trống. Vì vậy, nó phàn nàn với "quá nhiều giá trị để giải nén (dự kiến ​​là 0)".

Tương tự như vậy, trong [a] = ""trường hợp, bạn có một chuỗi rỗng, vì vậy không có gì để giải nén thực sự, nhưng bạn đang giải nén nó trên một biến, điều này một lần nữa là không thể. Đó là lý do tại sao nó phàn nàn "cần nhiều hơn 0 giá trị để giải nén".

Ngoài điều đó, như bạn đã nhận thấy,

>>> [] = ()

cũng không có lỗi, bởi vì ()là một bộ giá trị rỗng.

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

và khi nó được giải nén trong một danh sách trống, không có gì để giải nén. Vì vậy, không có lỗi.


Nhưng, khi bạn làm

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

như thông báo lỗi cho biết, bạn đang cố gắng gán cho một chuỗi ký tự. Điều nào là không thể. Đó là lý do tại sao bạn đang nhận được lỗi. Nó giống như nói

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

Nội bộ

Trong nội bộ, thao tác gán này sẽ được dịch sang UNPACK_SEQUENCEmã op,

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

Ở đây, vì chuỗi trống, thời gian UNPACK_SEQUENCEgiải nén 0. Nhưng khi bạn có một cái gì đó như thế này

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

trình tự 123được giải nén vào ngăn xếp, từ phải sang trái. Vì vậy, đỉnh của ngăn xếp sẽ là 1và tiếp theo sẽ là 2và cuối cùng sẽ là 3. Sau đó, nó gán lần lượt từ trên cùng của ngăn xếp cho các biến từ biểu thức bên trái.


BTW, trong Python, đây là cách bạn có thể thực hiện nhiều phép gán trong cùng một biểu thức. Ví dụ,

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

điều này hoạt động bởi vì, các giá trị bên phải được sử dụng để tạo một bộ tuple và sau đó nó sẽ được giải nén trên các giá trị bên trái.

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

nhưng kỹ thuật hoán đổi cổ điển a, b = b, asử dụng việc xoay vòng các phần tử ở trên cùng của ngăn xếp. Nếu bạn chỉ có hai hoặc ba phần tử thì chúng được xử lý bằng hướng dẫn ROT_TWOvà đặc biệt ROT_THREEthay vì xây dựng bộ tuple và giải nén.

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

Bạn cũng có thể sử dụng dis('[] = ""')mà không cần gọi điện compile().
Andrea Corbellini

Bạn có thể mô tả điều gì sẽ xảy ra nếu bạn hoán đổi nhiều hơn ba biến / phần tử, bằng cách sử dụng phương pháp trong ví dụ cuối cùng của bạn không?
nanofarad

@hexafraction Nó sẽ tạo một bộ tuple mới với tất cả các phần tử ở phía bên phải và sau đó nó sẽ giải nén chúng qua các biến ở phía bên trái.
thefourtheye

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.