Почему я не могу присвоить именованное выражение (оператор моржа LHS)?
Присваивание выражениям (в отличие от имен) в Python обычное дело. Например, это совершенно правильный синтаксис:
my.object["with_some"].very_long["expression"] = func(my.object["with_some"].very_long["expression"], my.object["with_some"].very_long["expression"])
однако, если я попытаюсь сократить его с помощью оператора моржа, сделав LHS именованным выражением, например
(x:=my.object["with_some"].very_long["expression"]) = func(x, x)
Python вызывает SyntaxError:
SyntaxError: невозможно присвоить именованному выражению
Так же,
for x[0] in range(5)
допустимый синтаксис (сильно запутанный), тогда как
for (a:=x[0]) in range(5)
снова
SyntaxError: cannot assign to named expression
.
Почему я не могу назначить именованное выражение? Это задумано или реализовано?
PEP 572 упоминает некоторые случаи, когда оператор моржа не может быть использован, но все, кроме одного, относятся к синтаксису выражений без скобок, а последний - о f-строках. В отличие от ситуации, указанной в этом ответе ((self.x := ...)
), цель назначения в операторе моржа в моем случае - это простое имя / идентификатор, а не выражение. Из справочника по языку также не ясно, почему это запрещено. Поиск в Google сообщения об ошибке сегодня дает ровно три результата на момент написания: одна проблема с ограничениями в понимании, сообщение чата переполнения стека, ожидающее сотен горячих сетевых вопросов (чего не произошло), и проблема в стороннем Python парсер; никто мне не поможет.
По какой причине я не могу присвоить именованное выражение? Это правило проектирования, которое где-то задокументировано или определено, или это ограничение реализации? Насколько я понимаю, это не приводит к двусмысленностям, и, казалось бы, мой вариант использования должен быть допустимым.
3 ответа
my.object["with_some"].very_long["expression"] = \
func(my.object["with_some"].very_long["expression"],
my.object["with_some"].very_long["expression"])`
синтаксический сахар для
my.object["with_some"].very_long.__setitem__(
"expression",
func(my.object["with_some"].very_long["expression"],
my.object["with_some"].very_long["expression"]))
так что это не так симметрично, как вы думаете. В оригинальном длинном выражении в значении, а не само выражение, передаются в качестве двух аргументов
func
, А само исходное выражение только вид мишени уступки.
Однако вы можете написать
x["expression"] = func(
(x:=my.object["with_some"].very_long)["expression"],
x["expression"])
с участием
x
присваивается значение одного общего выражения обессахаренной версии,
my.object["with_some"].very_long
.
Выражение присваивания должно быть справа от присваивания, потому что оно вычисляется перед левой стороной. Кроме того, это должен быть первый аргумент, который использует
:=
, потому что аргументы функции гарантированно оцениваются слева направо.
Вот тест, который я использовал, чтобы убедиться, что вышеуказанное должно работать, если я определил
A
соответственно.
class A:
def __init__(self, y):
self.b = dict(foo=y)
def func(x, y):
return x + y
a = A(A("bar"))
x["foo"] = func((x:=a.b["foo"].b)["foo"], x["foo"])
Новое значение
a.b["foo"].b["foo"]
является
"barbar"
, как и следовало ожидать из определения
func
.
Оператор моржа назначает только в качестве побочного эффекта - его значение не является левой переменной, а является результатом его выражения в правой части.
Итак, значение названного выражения
(x:=my.object["with_some"].very_long["expression"])
является результатом правой части оператора моржа (:=
), т.е. результат выражения
my.object["with_some"].very_long["expression"]
Обозначим это как
result
, так что ваша команда
(x:=my.object["with_some"].very_long["expression"]) = func(x, x)
такой же как
result = func(x, x)
В настоящее время,
result
это значение, а не имя переменной. Может быть
None
, может быть, число, может быть конкретный список или что-то еще, но не допускается в левой части оператора присваивания.
Таким образом, присвоение именованному выражению (по крайней мере, в большинстве случаев) бессмысленно и по этой причине не допускается.
Сравните:
my.object["with_some"].very_long["expression"] = func(7, 7) # OK
с участием
7 = func(7, 7) # error
Объяснение:
В вашем первом выражении
my.object["with_some"].very_long["expression"] = \ func(my.object["with_some"].very_long["expression"], my.object["with_some"].very_long["expression"])
вы хотите изменить значение атрибута вашего объекта (на основе значения его текущего атрибута), т.е. сделать что-то как
my.object["with_some"].very_long["expression"] = func(7, 7)
Во втором выражении
(x:=my.object["with_some"].very_long["expression"]) = func(x, x)
левая часть оператора присваивания
(=)
НЕ является атрибутом вашего объекта, а является его значением. Итак, вы попытались присвоить что-то значение, т.е. сделать что-то как7 = func(7, 7)
Из PEP 572 - Выражения присваивания, Аннотация:
Это предложение по созданию способа присвоения переменных в выражении...
В LHS назначения нет упоминания о его использовании.