I have the following expression:
>>> a = 3
>>> b = 2
>>> a == (a := b)
False
Now, a == 2
after the operation, as expected. And the result is what I would want, i.e., comparison of a
to RHS of assignment before assignment.
Reversing the order of the equality operator reverses the result:
>>> a = 3
>>> b = 2
>>> (a := b) == a
True
There does not appear to be anything directly relevant to this corner case in PEP-572, relative precedence section. The next section, change to evaluation order mentions that the evaluation order is left-to-right. Is that what is going on here (stash the value of a
, update it, and compare vs update a
, then compare against its new value)?
Where is this behavior defined, and how reliable is it?
Neither of those PEP sections have to do with this. You just have a ==
comparison, and the general Evaluation order applies: "Python evaluates expressions from left to right."
So your (a := b) == a
simply first evaluates the left side (a := b)
, assigning something to a
and evaluating to the same value. And then evaluate the right side a
, which is of course still the same (just-assigned) value, so you get True
.
About those PEP sections:
What that first PEP section says is that :=
groups less tightly than ==
, so it would apply if you didn't have parentheses:
a == a := b
would mean (a == a) := b
(you'd get a syntax error for trying to assign to a comparison).a := b == a
would mean a := (b == a)
, where with your values the b == a
evaluates to False
and that gets assigned to a
and becomes the result of the entire expression. (Note that at statement-level, you'd have to write (a := b == a)
.)What that second PEP section does is just to point out something bad that had already existed but which the :=
made "more visible", so they suggested to finally fix it. The issue was that a dict comprehension like {X: Y for ...}
evaluated Y
before X
, against the general left-to-right rule and against dict displays like {X: Y}
which already did evaluate X
before Y
as expected. Consider this:
>>> a, b = 3, 2
>>> {a: (a := b) for _ in '_'}
{3: 2}
With that old behavior, it would've resulted in {2: 2}
. And given that people might write something like that when :=
became available, it became more of an issue.