0

分配给表达式(而不是名称)在 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:无法分配给命名表达式

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

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

PEP 572提到了一些不能使用海象运算符的情况,但除了一种情况外,所有情况都是关于无括号表达式的语法,最后一种情况是关于 f 字符串。与此答案( )中指出的情况不同,(self.x := ...)在我的情况下,海象运算符中的赋值目标是一个简单的名称/标识符,而不是表达式。从语言参考中也不清楚为什么不允许这样做。 今天在谷歌上搜索错误消息在撰写本文时恰好产生了三个结果:一个关于理解限制的问题,一个Stack Overflow 聊天消息,期望有数百个热门网络问题(没有发生),以及第 3 方 Python 解析器中的问题;没有人帮助我。

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

4

3 回答 3

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.__setitem__(
    "expression",
    func(my.object["with_some"].very_long["expression"], 
         my.object["with_some"].very_long["expression"]))

所以它不像你想象的那么对称。原始 long 表达式的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

于 2020-11-25T14:31:32.527 回答
1

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

所以命名表达式的值

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

是海象运算符( )右边:=的结果,即表达式的结果

my.object["with_some"].very_long["expression"]

让我们表示 as result,所以你的命令

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

是相同的

result = func(x, x)

现在,result是一个,而不是一个变量名。也许None,也许是一个数字,也许是一个特定的列表或其他东西,但不允许在赋值运算符的左侧。

因此,分配给命名表达式(至少在大多数情况下)是没有意义的,因此不允许。

于 2020-11-25T13:56:39.280 回答
0

相比:

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 - 赋值表达式,摘要

这是一个关于创建一种分配给表达式中的变量的方法的提议......

没有提到在分配的 LHS 中使用它。

于 2020-11-25T14:58:46.373 回答