Это следует правилам раздела инструкций присваивания из документации,
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
. Затем он назначает переменные из левого выражения одну за другой сверху стека.
Кстати, в 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()
.