Python Несколько Операторов Присваивания В Одной Строке
(Не волнуйтесь, это не еще один вопрос о распаковке кортежей.)
в python, заявление, как foo = bar = baz = 5
присваивает переменным foo, bar и baz значение 5. Он присваивает эти переменные слева направо, что может быть доказано более неприятными примерами, такими как
>>> foo[0] = foo = [0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
>>> foo = foo[0] = [0]
>>> foo
[[...]]
>>> foo[0]
[[...]]
>>> foo is foo[0]
True
но ссылка на язык python заявляет, что операторы присваивания имеют форму
(target_list "=")+ (expression_list | yield_expression)
и по поручению expression_list
сначала оценивается, а затем присваивается происходит.
так как строка foo = bar = 5
быть действительным, учитывая, что bar = 5
не expression_list
? Как эти несколько назначений в одной строке анализируются и оцениваются? Я неправильно читаю ссылку на язык?
4 ответов
весь кредит идет @MarkDickinson, который ответил на это в комментарии:
уведомления
+
на(target_list "=")+
, что означает одну или несколько копий. Вfoo = bar = 5
, есть два(target_list "=")
производств, аexpression_list
просто5
все target_list
productions (т. е. вещи, которые выглядят как foo =
) в инструкции присваивания получить назначение, слева направо, на expression_list
в правом конце заявления, после expression_list
получает оцененный.
и, конечно, обычный синтаксис назначения "кортеж-распаковка" работает в этом синтаксисе, позволяя вам делать такие вещи, как
>>> foo, boo, moo = boo[0], moo[0], foo[0] = moo[0], foo[0], boo[0] = [0], [0], [0]
>>> foo
[[[[...]]]]
>>> foo[0] is boo
True
>>> foo[0][0] is moo
True
>>> foo[0][0][0] is foo
True
Марк Дикинсон объяснил синтаксис того, что происходит, но странные примеры с участием foo
показывают, что семантика может быть нелогичным.
В C,=
является правоассоциативным оператором, который возвращает в качестве значения RHS назначения, поэтому, когда вы пишете x = y = 5
, y=5
сначала оценивается (присвоение 5 y
в процессе) и это значение (5) затем присваивается x
.
прежде чем я прочитал этот вопрос, я наивно полагал, что примерно то же самое происходит в Python. Но, в Python =
не выражение (например, 2 + (x = 5)
ошибка синтаксиса). Таким образом, Python должен достичь нескольких назначений другим способом.
мы можем разобрать, а не угадать:
>>> import dis
>>> dis.dis('x = y = 5')
1 0 LOAD_CONST 0 (5)
3 DUP_TOP
4 STORE_NAME 0 (x)
7 STORE_NAME 1 (y)
10 LOAD_CONST 1 (None)
13 RETURN_VALUE
посмотреть этой для описания инструкций байтового кода.
первая инструкция толкает 5 в стек.
вторая инструкция дублирует его ... так теперь в верхней части стека есть два 5s
STORE_NAME(name)
"реализует name = TOS" в соответствии с документацией байтового кода
STORE_Name(x)
осуществляет x = 5
(5 сверху стека), выскакивая, что 5 из стека, как он идет, после чего STORE_Name(y)
осуществляет y = 5
С другими 5 в стеке.
остальные байткод не прямое отношение.
в случае foo = foo[0] = [0]
байт-код более сложный из-за списки, но имеет принципиально схожую структуру. Ключевым замечанием является то, что однажды list [0]
создается и помещается в стек, затем инструкция DUP_TOP
не помещает другой скопировать of [0]
на стеке, вместо этого он помещает другой ссылка в список. Другими словами, на этом этапе два верхних элемента стека являются псевдонимами для одного и того же списка. Это наиболее ясно видно в несколько более простом случае:
>>> x = y = [0]
>>> x[0] = 5
>>> y[0]
5
когда foo = foo[0] = [0]
выполняется, списка [0]
сначала назначен foo
и затем псевдоним того же списка присваивается foo[0]
. Вот почему это приводит к foo
быть циклических ссылок.
bar = 5
не является выражением. Множественное присвоение-это отдельный оператор от оператора присваивания; выражение-это все справа от самого правого =
.
хороший способ подумать об этом-это то, что самое правильное =
является основным разделителем; все справа от него происходит слева направо, и все слева от него происходит слева направо.
https://docs.python.org/3/reference/simple_stmts.html#grammar-token-assignment_stmt
оператор присваивания оценивает список выражений (помните, что это может быть одно выражение или список, разделенный запятыми, последний дает кортеж) и назначает один результирующий объект каждому из целевых списков слева направо.