12

我刚刚了解到新的海象运算符 ( :=) 不能用于设置实例属性,它应该是无效的语法(引发 a 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
4

2 回答 2

11

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

这是一个提议,用于创建一种使用符号为表达式中的变量NAME := expr赋值的方法。

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

语法和语义部分进一步指定它:

NAME是一个标识符。

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

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

setattr(self, 'foo', temp)

这就是允许您为属性定义 getter 和 setter 的原因。如果它必须使用具有自定义设置器的属性,它将使赋值表达式的规范和实现变得复杂。

例如,如果 setter 转换了被赋值的值,那么赋值表达式的值应该是原始值还是转换后的值?

另一方面,变量不能自定义。分配给变量始终具有相同的简单语义,并且表达式很容易计算出所分配的值。

同样,您不能将海象运算符与切片分配一起使用。这是无效的:

foo1[2:4] := foo2[1:3]
于 2020-09-24T22:41:40.160 回答
4

有趣的是,禁止海象 object.attributes 似乎只存在于 Python 的解析器中,它将文本代码解析为抽象语法树 (ast),然后将被编译和执行。如果您手动创建一个 ast 语法树self.foo来代替varin(var := temp)和 thencompileexec该树,它会按照您的直觉预期编译和执行。

显然,底层的功能是允许将海象分配给 object.attributes,他们只是选择不让我们使用它,因为他们担心它会让人写出令人困惑的代码或其他东西。非常感谢...

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

于 2021-03-13T19:45:33.103 回答