В python 3.4, я печатаю
[] = ""
и он отлично работает, не возникает Исключение. Хотя, конечно, []
после этого не равно ""
.
[] = ()
также отлично работает.
"" = []
вызывает исключение, как ожидалось,
() = ""
вызывает исключение, как ожидалось. Итак, что происходит?
Ответ 1
Вы не сравниваете для равенства. Вы назначаете.
Python позволяет назначать несколько целей:
foo, bar = 1, 2
присваивает два значения foo
и bar
соответственно. Все, что вам нужно, это последовательность или итерация с правой стороны, а также список или кортеж имен слева.
Когда вы выполните:
[] = ""
вам была присвоена пустая последовательность (пустые строки - это последовательности) - пустой список имен.
Это, по сути, то же самое, что и делать:
[foo, bar, baz] = "abc"
где вы заканчиваете с foo = "a"
, bar = "b"
и baz = "c"
, но с меньшим количеством символов.
Однако вы не можете назначить строку, поэтому ""
в левой части присваивания никогда не работает и всегда является синтаксической ошибкой.
Смотрите Документация операторов присваивания:
Оператор присваивания оценивает список выражений (помните, что это может быть одно выражение или список, разделенный запятыми, последний из которых имеет кортеж) и присваивает единственный результирующий объект каждому из целевых списков слева направо.
и
Назначение объекта целевому списку , необязательно заключенному в круглые скобки или квадратные скобки, рекурсивно определяется следующим образом.
Акцент на мой.
То, что Python не бросает синтаксическую ошибку для пустого списка, на самом деле является частью ошибки! Официально документированная грамматика не допускает пустой целевой список, а для пустого ()
вы получите ошибку. См. ошибка 23275; это считается безобидной ошибкой:
Исходная точка - это признание того, что это было очень долго и безвредно.
Также см. Почему это допустимо для назначения пустого списка, но не для пустого кортежа?
Ответ 2
В соответствии с правилами инструкций Assignment из документации,
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
op code,
>>> 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