赋值表达式(海豹运算符)的评估顺序

8

我有以下表达式:

>>> a = 3
>>> b = 2
>>> a == (a := b)
False

现在,在这个操作之后,a == 2,与预期相符。结果正是我想要的,即将a与赋值右侧比较后再进行赋值。

反转等号操作符的顺序会反转结果:

>>> a = 3
>>> b = 2
>>> (a := b) == a
True

PEP-572的相对优先级部分中,似乎没有直接涉及到这个角落案例的相关内容。下一部分改变评估顺序提到评估顺序是从左到右。这里是否也是这样做的(隐藏a的值,更新它并与更新a的值进行比较,然后与其新值进行比较)?

这种行为在哪里定义,可靠性如何?


10
对我来说,这似乎只是“<exp1>comp_op<exp2>”从左到右进行评估的一个结果。 - juanpa.arrivillaga
@juanpa.arrivillaga。经过更多的思考,我开始意识到“评估”的含义。最初我感到困惑是因为语法问题。我会保留这个问题,因为它可能仍然有用。 - Mad Physicist
4
不仅针对:=操作符,对于a == f(b)f(b) == a也会产生相同的行为,其中f是改变全局变量a的函数(或者在具有++/--的语言中针对a == ++a++a == a)。 - tobias_k
1
@tobias_k:实际上,在像C/C++这样的语言中,类似a == ++a++a == a的东西通常不是有序的;语言可以选择以任何顺序评估它们(我认为最新的C++标准做了一些事情使得这样的东西有序化,但这是一个非常近期的变化);a == ++a可以先评估左边或右边,整个表达式将根据编译器决定首先执行哪个而评估为true或false。例如,请参见Is C == C++ undefined behaviour?。Python在左到右规则方面比大多数语言更严格。 - ShadowRanger
@ShadowRanger 感谢您指出这一点,我很懒,只尝试了Java... - tobias_k
1个回答

7
那两个PEP章节都与此无关。你只是进行了一个==比较,而一般的“表达式求值顺序”适用: “Python从左到右评估表达式。” 因此,你的(a := b) == a首先仅评估左侧,即将某些东西赋给a并评估为相同值。 然后评估右侧a,它当然仍然是相同(刚分配)的值,因此您将获得True
关于那些PEP章节:
第一个PEP章节说的是:===结合更松散,因此如果没有括号,它才会应用:
  • a == a := b表示(a == a) := b(您会因尝试对比较进行分配而得到语法错误)。
  • a := b == a表示a := (b == a),其中对于您的值,b == a计算为False,并将其分配给a,并成为整个表达式的结果。(请注意,在语句级别,您必须编写(a := b == a)。)
第二个PEP章节所做的只是指出已经存在的一个不好的问题,但:=使其变得“更明显”,因此他们建议最终修复它。该问题是字典推导式(如{X: Y for ...})在一般从左到右的规则和像{X: Y}这样的字典显示预期评估X之前评估Y,而:=使其更加明显。
>>> a, b = 3, 2
>>> {a: (a := b) for _ in '_'}
{3: 2}

使用旧的行为,它将会导致{2: 2}。并且由于人们可能在可用:=时编写类似的内容,这就成为了一个更大的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接