Embercode
✕
OperatorsLesson 7 of 14

30 min read

Evaluation order and the walrus operator in Python

Why this matters

A line of code with an = or a += changes a variable. A line with two such changes depends on the order in which the parts run. A programmer who does not know that order can write a line that looks right and prints a different number from the one expected. In many languages the best-known case is i = i++. It looks as if it adds one to i, and in fact it does nothing. Python does not have ++, so that exact line is a syntax error. The same kind of surprise still appears with the walrus operator :=, which assigns a name and also gives the value, so it can sit inside a bigger expression.

The lesson on assignment showed that := gives the new value. This lesson covers the order. It asks which part of a line runs first, and what that means for the value each part sees.

Two terms: operand and expression

An operand is a value that an operator works on. In 2 + 3, the operands are 2 and 3, and the operator is +.

An expression is a piece of code that gives one value. 2 + 3 is an expression with the value 5. (x := 5) is an expression too. Its value is 5, and as a side effect the name x now refers to 5.

The first rule: the left operand runs first

When an operator has two operands, Python works out the left one before the right one. The Python language reference states this order for binary operators, which are operators with two operands such as +.

Python · editable

The output is:

y = 6 x = 5

Python starts with the left operand, which is x. It reads the value 1 and keeps it. Then it works out the right operand, (x := 5). That stores 5 in x, and the value of the expression is 5. The addition is 1 + 5, which is 6. The left x was read before the assignment, so the old value 1 is used.

Now swap the order.

Python · editable

The output is z = 10. This time the left operand is the assignment, so x becomes 5 first. The right x then reads 5, and the sum is 5 + 5.

The two lines show the rule. The result depends on the position of the parts, not on which part looks more important. Read the expression from left to right, and work out each operand in turn.

The second rule: Python works out the value before the place

An assignment has two sides. The left side says where to store. The right side gives the value to store. For a plain name such as x = value, the place is simply the name x. For an element such as a[index] = value, the place depends on index, and that index is worked out when Python stores the value, which is after the right side has been worked out.

This is the one place where Python's order differs from many other languages. In those languages the index on the left is usually worked out first. Python evaluates the whole right side, then the index on the left, then stores.

A list is a row of numbered slots, and the index is the number of the slot. Indexes start at 0, so arr[0] is the first slot.

Python · editable

The output is:

[0, 0, 0] i = 0

Here the right side i is 0, and the index is also 0, so the value 0 goes into arr[0]. The next example uses the walrus to change j on the right side. The change happens before the index is read:

Python · editable

The output is:

[0, 1, 0] j = 1

Here is the order of the steps.

  1. Python works out the value on the right. The walrus makes j 1 and gives 1.
  2. Python works out the place. The index is j, which is now 1, so the place is arr2[1].
  3. Python stores the value 1 in arr2[1].

A harder case has the same rule with an assignment on the right:

Python · editable

The output is:

[10, 20, 2] p = 2

The right side (p := 2) runs first, so p becomes 2 before the place is found. The place is a[2], and the value 2 is stored there.

These results differ from many other languages, where the place a[0] would be found first. Write the place with a name that does not change on the right side, or split the work over two lines, so a reader does not have to remember this order.

The third rule: arguments run left to right

A function is a named block of code that you call with values. The values you pass in are called arguments. Python works out the arguments from left to right, and then it runs the function with the results.

Python · editable

The output is:

r = 12 k = 2

The first argument k is read as 1. The second argument (k := k + 1) makes k 2 and gives 2. So the function receives 1 and 2, and it returns 1 times 10 plus 2, which is 12.

Why i = i++ cannot be written in Python

In Java, which has ++, the line i = i++ with i set to 4 does this: the right side gives the old value 4 and makes i equal to 5, and then the assignment stores the 4 back into i. The increment is undone.

Python has no ++ operator, so the same line is a syntax error:

Python · editable

The output is:

  File "/path/to/main.py", line 2
    i = i++
           ^
SyntaxError: invalid syntax

Python refuses the line before it runs, so no value changes. The trap has a walrus form that does run and does nothing. Here (i := i + 1) makes i 5 and gives 5, then the expression subtracts 1, which gives 4, and the assignment stores 4 back into i:

Python · editable

The output is:

4

The steps look like this.

StepWhat runsValue of i after the step
1The walrus makes i 5 and gives 55
2The subtraction gives 5 - 1, which is 45
3The store puts the 4 into i4

The new point here is the order. The walrus runs in the middle, and the store runs at the end, so the store wins. Python gives no warning for this line. To add one, write i += 1 on its own line.

A longer line with several changes

Here is a line that reads and changes the same name more than once.

Python · editable

The output is:

19 4

Multiplication runs before addition, as the arithmetic lesson showed. The line is therefore x + ((x := x + 1) * x). Trace it one step at a time.

StepWhat runsx after the stepResult so far
1The left operand x is read as 333
2(x := x + 1) makes x 4, and gives 443
3The x on the right of * is read as 4, so the product is 4 times 4416
4The sum is 3 plus 16, which is stored in total419

Each step reads the value that exists at that moment. Nothing is read from the start of the line.

Checking the order with a function that prints

Reading the rules is not the same as seeing them run. A function that prints when it is called shows the order directly. The function below prints its name, then returns the number it was given, so each call leaves a line in the output.

Python · editable

The output is:

read left
read right
sum 3
read value
read index
[7, 0, 0]
read a
read b
read c
r 16
read x
read y
total 3

The first pair shows that the left operand of + is called first. The array line shows that Python calls tag("value", 7) before tag("index", 0), even though the index appears on the left of the assignment in the code. That is the second rule in action. The third group shows that tag("b", 2) and tag("c", 3) both run before the addition, because the multiplication * is worked out first and its operands run left to right. The last pair shows the same rule for subtraction.

Use this trick when a line is hard to read. Put a print in a function, run the program, and read the output in order. Remove the function when you are done.

Common mistakes

Mistake 1: the walrus written where a plain assignment is meant

The line i = (i := i + 1) - 1 looks like an increment, but it leaves the name as it was. The program does not stop and prints no warning. The symptom is a counter that never moves. The fix is i += 1 on its own line.

Mistake 2: assuming the index is found first

A reader who has used other languages may expect the place arr[index] to be found before the right side. In Python the right side runs first. The example a[p] = (p := 2) stores into a[2], not into a[0]. Avoid writing a walrus or a call with side effects on the right when the same name is used as an index on the left.

Mistake 3: a number on the left of an increment or an assignment

Python does not allow a value on the left of =, and it does not have ++. Each line below is a syntax error:

Python · editable

The output is:

  File "/path/to/main.py", line 2
    5 = x
    ^
SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='?

The message says that a literal number cannot be assigned to. The number 5 is a value, and it has no name to change. The line number in the message is the line of the mistake in your file.

Python · editable

The output is:

  File "/path/to/main.py", line 1
    print(5++)
             ^
SyntaxError: invalid syntax

Python reads 5++ as 5 followed by a second plus sign that has nothing after it, so it stops there.

Writing code that does not depend on the order

The safest way to avoid these traps is to give each change its own line. This loop fills a list with squares. Each line changes one thing, and no line depends on the order of its parts.

Python · editable

The output is:

[0, 1, 4, 9, 16]

The line squares[i] = i * i reads i but does not change it. The line i += 1 changes i on its own, after the store is done. A reader can check each line by reading it once. In Python, a list comprehension, which builds a list in one expression, such as [i * i for i in range(5)] gives the same list in one line, and it changes no name at all.

Practice

Exercise 1. Predict the output of this code.

a = 2
b = a * (a := 3)
print(b)

Show the answer

The output is 6. The left operand a is read as 2. Then (a := 3) sets a to 3. The product is 2 times 3.

Exercise 2. Predict the value of n after this code runs.

n = 10
n = n - (n := n - 1)
print(n)

Show the answer

The output is 1. The left n is read as 10. The walrus makes n 9 and gives 9. The difference is 10 minus 9, which is 1, and that is stored in n.

Exercise 3. Predict the contents of v and the value of q after this code runs.

v = [5, 6, 7]
q = 0
v[q] = (q := q + 2)
print(v, "q =", q)

Show the answer

The output is [5, 6, 2] q = 2. The right side runs first, so q becomes 2, and the place is v[2]. The value 2 is stored there.

Exercise 4. The function show from this lesson returns a * 10 + b. Predict the value of r after this code runs.

def show(a, b):
    return a * 10 + b
 
k = 1
r = show(k, (k := k + 1))
print("r =", r, "k =", k)

Show the answer

The output is r = 12 k = 2. The first argument is read as 1 before the walrus. The second argument makes k 2 and gives 2. The function receives 1 and 2, so the result is 12.

Exercise 5. Using the tag function from the section above, predict the lines printed by this code, in order, and the final value of q.

q = tag("p", 2) + tag("q", 3) * tag("r", 4)
print("q", q)

Show the answer

The printed lines are read p, read q, read r, and then q 14. The left operand tag("p", 2) is worked out first. Then the multiplication runs its left operand tag("q", 3) and its right operand tag("r", 4). The product is 12, and the sum is 2 plus 12, which is 14.

What you now know

  • Python works out the left operand of an operator before the right one.
  • For a[index] = value, Python works out the value first, then the index, then stores. This differs from many other languages.
  • Function arguments are worked out from left to right before the function runs.
  • Python has no ++, so i = i++ is a syntax error. The walrus := can still make a line that reads a name and changes it in the middle.
  • A line that changes a name more than once is hard to read, so give each change its own line.
Practice this topic: coding problems for Operators →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.