Почему я не могу присвоить именованное выражение (оператор моржа 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

Объяснение:

  1. В вашем первом выражении

    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)
    
  2. Во втором выражении

    (x:=my.object["with_some"].very_long["expression"]) = func(x, x)
    

    левая часть оператора присваивания (=)НЕ является атрибутом вашего объекта, а является его значением. Итак, вы попытались присвоить что-то значение, т.е. сделать что-то как

    7 = func(7, 7)
    

Из PEP 572 - Выражения присваивания, Аннотация:

Это предложение по созданию способа присвоения переменных в выражении...

В LHS назначения нет упоминания о его использовании.

Другие вопросы по тегам