为什么不能使用Python的海象运算符来设置实例属性?

Why can't Python's walrus operator be used to set instance attributes?

我刚刚了解到新的海象运算符 (:=) 不能用于设置实例属性,它应该是无效语法(引发 SyntaxError)。

这是为什么?(你能提供一个 link 给提到这个的官方文档吗?)

我查看了 PEP 572,但找不到 if/where 这已记录在案。


研究

提及此限制但没有解释或来源:

you can't use the walrus operator on object attributes


示例代码

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

PEP 572 描述了这个(强调我的)的目的:

This is a proposal for creating a way to assign to variables within an expression using the notation NAME := expr.

self.foo 不是变量,它是对象的属性。

Syntax and semantics 部分进一步说明:

NAME is an identifier.

self.foo 不是标识符,它是由 . 运算符分隔的两个标识符。

虽然我们经常类似地使用变量和属性,有时会草率地将 self.foo 称为变量,但它们并不相同。分配给 self.foo 实际上只是一个 shorthand for

setattr(self, 'foo', temp)

这允许您为属性定义 getter 和 setters。如果它必须使用自定义 setters.

的属性,它将使赋值表达式的规范和实现复杂化

例如,如果 setter 转换正在赋值的值,赋值表达式的值应该是原始值还是转换后的值?

另一方面,变量无法自定义。对变量赋值始终具有相同、简单的语义,并且表达式很容易计算出赋值。

同样,您不能将海象运算符用于切片赋值。这无效:

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

有趣的是,海象object.attributes的禁令似乎只存在于Python的解析器中,它将文本代码解析为抽象语法树(ast),然后将被编译和执行.如果您手动创建一个 ast 语法树,用 self.foo 代替 (var := temp) 中的 var 然后 compileexec 该树,它编译并执行正如您直觉所期望的那样。

很明显,底层功能允许对 object.attributes 进行海象赋值,他们只是选择不让我们使用它,因为他们担心这会让人们写出令人困惑的代码或其他东西。非常感谢...

所以无论如何,一个极端的(完全不推荐!)解决方案是让你做一些预编译 ast-surgery 将你的 object.attribute 目标拼接到你的海象运算符中,然后它正如您所料,可能 运行。 (我发现这一点是因为我已经在做 ast-surgery,出于其他原因用 object.attributes 替换了简单变量,我很高兴发现海象赋值仍然有效!)