Multiple assignment and evaluation order in Python
In an assignment statement, the right-hand side is always evaluated fully before doing the actual setting of variables. So,
x, y = y, x + y
evaluates y
(let's call the result ham
), evaluates x + y
(call that spam
), then sets x
to ham
and y
to spam
. I.e., it's like
ham = y
spam = x + y
x = ham
y = spam
By contrast,
x = y
y = x + y
sets x
to y
, then sets y
to x
(which == y
) plus y
, so it's equivalent to
x = y
y = y + y
Python - Multiple Assignment
a = 0
b = 1
while a < 10:
print(a)
a = b
b = a + b
In this case, a
becomes b
and then b
becomes the changed a
+ b
a, b = 0, 1
while a < 10:
print (a)
a, b = b, a+b
In this case, a
becomes b
and at the same time b
becomes the original
a + b.
That's why, in your case b
becomes the new a + b
, which, since a = b
, basically means b = b + b
. That's why the value of b
doubles everytime.
Tricky order of evaluation in multiple assignment
Yes, you should be able to rely on this.
From the reference documentation
If the target list is a comma-separated list of targets: The object must be an iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.
(emphasis mine)
k[s], k[s] = k[s], None
is equivalent to
t = k[s]
k[s] = t
k[s] = None
Side note: The evaluation order also holds when the right side is a dynamic iterable, e.g. a generator. All necessary elements will be extracted first and assigned left to right.
The mechanism behind multiple assignment in Python
a, b = c, d
is equivalent to
temp = (c, d)
a = temp[0] # Expression a is evaluated here, not earlier
b = temp[1] # Expression b is evaluated here, not earlier
Personally I would recommend to write complex assignments explicitly with temporary variables as you showed it.
Another way is to carefully choose the order of the elements in the assignment:
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]
Changes nums
as you expect.
Python Assignment Order
In the first version, when you write
a, b = b, a + b
the expression
b, a + b
is evaluated first. After it has been evaluated, it is subsequently assigned to a, b
with tuple unpacking. The key point is that the entire right hand side of an assignment statement is evaluated in its entirety before performing the binding of the left-hand side names.
In the second version,
a = b
# a is now re-bound and potentially has changed value
is performed first, and then
b = a + b
# the expression a + b uses the modified value of a
happens after. By which point, a
has been re-bound.
So, to illustrate with some values. The first time round the loop, after a, b = 0, 1
we have
# a == 0
# b == 1
a, b = b, a + b
Now, b, a + b
is 1, 1
. And so we have
a, b = 1, 1
But the alternative goes like this:
a = b
# same as a = 1
b = a + b
# same as b = 1 + 1
You ask in a comment:
How would Example 1 be rewritten into 2 statements?
The safest way is to introduce temporary variables. Like this:
a1 = b
b1 = a + b
# now we can assign to a and b
a = a1
b = b1
In this case you don't actually need two temporary variables, one will suffice, but this is the most general form.
This tuple unpacking is how you swap two values idiomatically in Python. In other languages you write:
temp = a
a = b
b = temp
In Python you write:
a, b = b, a
Splitting multiple assignment doesn't produce the same output
They aren't the same.
In multiple assignments such as x, y = y, x+y
, the right side is evaluated first. So x+y
is evaluated, then y
is set equal to x+y
.
Say x=0
and y=1
. Then, x, y = y, x+y
evaluates to x, y = 1, 0+1
, so x, y = 1, 1
. y = 1
.
In the second example, x = y
causes x = 1
, and the next line y = x+y
causes y = 2
.
Is Python multiple assignment really right-to-left?
The right hand side of the assignment is computed all before the assignment happens.
So first you compute (y, x+y)
which is (2, 3)
and then you unpack it into x
and y
.
See also what happens if on the left you have more than identifiers but also expressions:
>>> a = [1, 2]
>>> i = 1
>>> a[i], i = 3, 4
>>> a, i
([1, 3], 4)
Order of evaluation of assignment expressions (walrus operator)
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 meana := (b == a)
, where with your values theb == a
evaluates toFalse
and that gets assigned toa
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.
Related Topics
Loop "Forgets" to Remove Some Items
All Combinations of a List of Lists
"Inconsistent Use of Tabs and Spaces in Indentation"
Converting Unix Timestamp String to Readable Date
Why Does Integer Division Yield a Float Instead of Another Integer
Adding Python to Path on Windows
How to Get Json to Load into an Ordereddict
Save Plot to Image File Instead of Displaying It Using Matplotlib
Django Template How to Look Up a Dictionary Value With a Variable
Pygame Window Not Responding After a Few Seconds
Flask View Return Error "View Function Did Not Return a Response"
Return, Return None, and No Return At All
Find the Current Directory and File'S Directory
Identify Groups of Continuous Numbers in a List