Почему нельзя использовать оператор моржа в Python для установки атрибутов экземпляра?

Я только что узнал, что новый моржовый оператор (:=) нельзя использовать для установки атрибутов экземпляра, это предположительно недопустимый синтаксис (вызывает SyntaxError).

Почему это?(И можете ли вы предоставить ссылку на официальные документы, в которых упоминается это?)

Я просмотрел PEP 572 и не смог найти, документировано ли / где это.


Исследование

Этот ответ упоминает это ограничение без объяснения или источника:

нельзя использовать оператор моржа для атрибутов объекта


Образец кода

class Foo:
    def __init__(self):
        self.foo: int = 0

    def bar(self, value: int) -> None:
        self.spam(self.foo := value)  # Invalid syntax

    def baz(self, value: int) -> None:
        self.spam(temp := value)
        self.foo = temp

    def spam(self, value: int) -> None:
        """Do something with value."""

Пытаюсь импортировать Foo приводит к SyntaxError:

    self.spam(self.foo := value)
              ^
SyntaxError: cannot use assignment expressions with attribute

2 ответа

Решение

PEP 572 описывает цель этого (выделено мной):

Это предложение по созданию способа присвоения переменных в выражении с использованием нотации NAME := expr.

self.foo не переменная, это атрибут объекта.

В разделе " Синтаксис и семантика " это дополнительно уточняется:

NAME это идентификатор.

self.foo не идентификатор, это два идентификатора, разделенных . оператор.

Хотя мы часто используем переменные и атрибуты аналогичным образом и иногда небрежно ссылаемся на self.fooкак переменная они не совпадают. Назначение self.foo на самом деле просто сокращение для

setattr(self, 'foo', temp)

Это то, что позволяет вам определять геттеры и сеттеры для атрибутов. Это усложнило бы спецификацию и реализацию выражения присваивания, если бы ему пришлось работать с атрибутами, которые имеют настраиваемые установщики.

Например, если установщик преобразует присвоенное значение, должно ли значение выражения присваивания быть исходным или преобразованным значением?

С другой стороны, переменные не могут быть изменены. Присвоение переменной всегда имеет одну и ту же простую семантику, и выражение может легко оценить значение, которое было присвоено.

Точно так же вы не можете использовать оператор моржа с назначением срезов. Это неверно:

foo1[2:4] := foo2[1:3]

Интересно, что запрет на использование атрибутов object.attributes, похоже, присутствует только в парсере Python, который анализирует текстовый код в абстрактное синтаксическое дерево (ast), которое затем будет скомпилировано и выполнено. Если вы вручную создаете синтаксическое дерево ast с помощью self.foo заняв место var в (var := temp) а потом compile или же exec это дерево, оно компилируется и выполняется так, как вы интуитивно ожидали.

Таким образом, очевидно, что основная функциональность предназначена для того, чтобы разрешить назначение моржа для object.attributes, они просто решили не позволять нам его использовать, потому что боялись, что это заставит людей писать запутанный код или что-то в этом роде. Большое спасибо...

В любом случае, крайним (совсем не рекомендуемым!) Решением было бы для вас сделать небольшую предварительную компиляцию ast-gery, чтобы объединить целевые объекты object.attribute с вашими операторами моржа, и тогда он, вероятно, будет работать так, как вы ожидаете. .(Я обнаружил это, так как уже делал аст-хирургию, заменявшую простые переменные на object.attributes по другим причинам, и я был рад обнаружить, что назначение моржа все еще работает!)

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