Multiple Assignment and Evaluation Order in Python

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 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.



Related Topics



Leave a reply



Submit