为什么不能使用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
然后 compile
或 exec
该树,它编译并执行正如您直觉所期望的那样。
很明显,底层功能允许对 object.attributes 进行海象赋值,他们只是选择不让我们使用它,因为他们担心这会让人们写出令人困惑的代码或其他东西。非常感谢...
所以无论如何,一个极端的(完全不推荐!)解决方案是让你做一些预编译 ast-surgery 将你的 object.attribute 目标拼接到你的海象运算符中,然后它正如您所料,可能 运行。 (我发现这一点是因为我已经在做 ast-surgery,出于其他原因用 object.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
然后 compile
或 exec
该树,它编译并执行正如您直觉所期望的那样。
很明显,底层功能允许对 object.attributes 进行海象赋值,他们只是选择不让我们使用它,因为他们担心这会让人们写出令人困惑的代码或其他东西。非常感谢...
所以无论如何,一个极端的(完全不推荐!)解决方案是让你做一些预编译 ast-surgery 将你的 object.attribute 目标拼接到你的海象运算符中,然后它正如您所料,可能 运行。 (我发现这一点是因为我已经在做 ast-surgery,出于其他原因用 object.attributes 替换了简单变量,我很高兴发现海象赋值仍然有效!)