Чому помилка не привласнюється до порожнього списку (наприклад, [] = “”)?


110

У python 3.4 я набираю текст

[] = "" 

і це працює чудово, виняток не піднімається. Хоча, звичайно [], не дорівнює "".

[] = ()

також чудово працює.

"" = []

створює виняток, як очікувалося,

() = ""

Хоча створює виняток, як очікувалося. Отже, що відбувається?

Відповіді:


132

Ви не порівнюєте за рівність. Ви призначаєте .

Python дозволяє призначити кілька цілей:

foo, bar = 1, 2

призначає два значення відповідно fooі bar, відповідно. Все, що вам потрібно, - це послідовність або ітерабельність з правого боку, а список або набір імен зліва.

Коли ви робите:

[] = ""

Ви призначили порожню послідовність (порожні рядки все ще є послідовностями) до порожнього списку імен.

Це по суті те саме, що робити:

[foo, bar, baz] = "abc"

де ви закінчуєте foo = "a", bar = "b"і baz = "c", але з меншою кількістю символів.

Однак ви не можете призначити рядок, тому ""з лівої сторони призначення не працює і завжди є синтаксичною помилкою.

Дивіться заяви про призначення Документацію щодо :

Заява про присвоєння оцінює список виразів (пам’ятайте, що це може бути одиничне вираження або список, розділений комами, останній позначає кортеж) і призначає єдиний результуючий об’єкт кожному з цільових списків зліва направо.

і

Призначення об'єкта до цільового списку, необов'язково укладеного в круглі дужки або квадратні дужки , рекурсивно визначається наступним чином.

Наголос мій .

Те, що Python не видає синтаксичну помилку для порожнього списку, насправді є дещо помилкою! Офіційно задокументована граматика не дає порожнього списку цілей, а для порожнього ()ви отримуєте помилку. Дивіться про помилку 23275 ; це вважається нешкідливим клопом:

Вихідним моментом є визнання, що це існує вже дуже давно і нешкідливо.

Також див. Чому справедливо призначати порожній список, а не порожній кортеж?


36

Це дотримується правил розділу документа про призначення про призначення з документації,

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

Якщо target listсписок цілей - розділений комою: Об'єкт повинен бути ітерабельним із такою ж кількістю елементів, як і цілі в списку цілей, а елементи призначаються зліва направо відповідним цілям.

Об'єкт повинен бути послідовністю з такою ж кількістю елементів, як і цілі в списку цілей, а елементи призначаються зліва направо відповідним цілям.

Отже, коли ти кажеш

[] = ""

"" є ітерабельним (будь-який дійсний рядок python є ітерабельним), і він розпаковується через елементи списку.

Наприклад,

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

Оскільки у вас є порожній рядок і порожній список, розпаковувати немає чого. Отже, жодної помилки.

Але, спробуйте це

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

У [] = "1"випадку ви намагаєтесь розпакувати рядок"1" через порожній список змінних. Тож він скаржиться на "занадто багато значень, щоб розпакувати (очікувано 0)".

Так само, у [a] = ""випадку, якщо у вас є порожній рядок, тому нічого не можна розпаковувати, але ви розпаковуєте його через одну змінну, що, знову ж таки, неможливо. Ось чому він скаржиться, що "потрібно розпакувати більше ніж 0 значень".

Крім того, як ви помітили,

>>> [] = ()

також не кидає помилок, тому що ()це порожній кортеж.

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

і коли він розпаковується через порожній список, розпаковувати нічого. Тож жодної помилки.


Але, коли ти

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

як йдеться у повідомленні про помилку, ви намагаєтеся призначити літеральний рядок. Що неможливо. Ось чому ви отримуєте помилки. Це як сказати

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

Внутрішні

Внутрішньо ця операція по призначенню буде переведена в UNPACK_SEQUENCEоп-код,

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

Тут, оскільки рядок порожній, раза UNPACK_SEQUENCEрозпаковується 0. Але коли у вас є щось подібне

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

послідовність 123розпаковується в стек, справа наліво. Отже, верхівка стека була б, 1а наступна була б 2і остання 3. Потім він присвоює зверху стека змінні з виразу лівої сторони по черзі.


BTW, в Python - це те, як ви можете виконувати кілька призначень в одному виразі. Наприклад,

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

це працює, тому що значення правої руки використовуються для побудови кортежу, і тоді вони будуть розпаковані над значеннями лівої сторони.

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

але класична техніка заміни a, b = b, aвикористовує обертання елементів у верхній частині стека. Якщо у вас є тільки два або три елементи , то вони обробляють спеціальними ROT_TWOі ROT_THREEінструкції замість побудови кортежу і розпакування.

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

Ви також можете користуватися dis('[] = ""')без дзвінків compile().
Андреа Корбелліні

Чи можете ви описати, що відбувається, якщо ви замінюєте більше трьох змінних / елементів, використовуючи метод у своєму останньому прикладі?
нанофарад

@hexafraction Він створить новий кортеж з усіма елементами з правого боку, а потім розпакує їх за змінними з лівого боку.
thefourtheye

@hexafraction: див. як
Martijn Pieters
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.