为什么我不能分配给命名表达式(LHS 海象运算符)?

Why can I not assign to a named expression (LHS walrus operator)?

在 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: cannot assign to named expression

同样,for x[0] in range(5) 是有效语法(只是非常混乱),而 for (a:=x[0]) in range(5) 又是 SyntaxError: cannot assign to named expression.

为什么我不能给命名表达式赋值?这是设计还是实现?

PEP 572 mentions some cases where the walrus operator cannot be used, but all but one are about the syntax of unparenthesised expressions and the final one is about f-strings. Unlike the situation pointed out in ((self.x := ...)), the assignment target within the walrus operator in my case is a simple name/identifier, not an expression. It's not clear from the language reference either why this is not allowed. Googling the error message today yields exactly three results at the time of writing: One issue about limitations in comprehensions, a Stack Overflow chat message expecting hundreds of Hot Network Questions (which didn't happen), and an issue in a 3rd-party Python parser; none帮帮我。

我无法分配给命名表达式的原因是什么?这是在某处记录或定义的设计规则,还是实施限制?据我所知,它不会导致任何歧义,而且我的用例似乎应该是有效的。

海象运算符仅作为副作用赋值 - 它的值不是左侧变量,而是它的右侧表达式。

所以命名表达式的值

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

是海象算子右边的结果:=),i。 e.表达式的结果

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(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"]))

所以它并不像你想象的那样对称。原始长表达式的 value,而不是表达式本身,作为两个参数传递给 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 的定义所预期的那样。

比较:

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 - Assignment Expressions, Abstract

This is a proposal for creating a way to assign to variables within an expression...

作业的LHS中没有提到使用它。