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 +.
The output is:
y = 6 x = 5Python 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.
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.
The output is:
[0, 0, 0] i = 0Here 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:
The output is:
[0, 1, 0] j = 1Here is the order of the steps.
- Python works out the value on the right. The walrus makes
j1 and gives 1. - Python works out the place. The index is
j, which is now 1, so the place isarr2[1]. - Python stores the value 1 in
arr2[1].
A harder case has the same rule with an assignment on the right:
The output is:
[10, 20, 2] p = 2The 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.
The output is:
r = 12 k = 2The 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:
The output is:
File "/path/to/main.py", line 2
i = i++
^
SyntaxError: invalid syntaxPython 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:
The output is:
4The steps look like this.
| Step | What runs | Value of i after the step |
|---|---|---|
| 1 | The walrus makes i 5 and gives 5 | 5 |
| 2 | The subtraction gives 5 - 1, which is 4 | 5 |
| 3 | The store puts the 4 into i | 4 |
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.
The output is:
19 4Multiplication runs before addition, as the arithmetic lesson showed. The line is therefore x + ((x := x + 1) * x). Trace it one step at a time.
| Step | What runs | x after the step | Result so far |
|---|---|---|---|
| 1 | The left operand x is read as 3 | 3 | 3 |
| 2 | (x := x + 1) makes x 4, and gives 4 | 4 | 3 |
| 3 | The x on the right of * is read as 4, so the product is 4 times 4 | 4 | 16 |
| 4 | The sum is 3 plus 16, which is stored in total | 4 | 19 |
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.
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 3The 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:
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.
The output is:
File "/path/to/main.py", line 1
print(5++)
^
SyntaxError: invalid syntaxPython 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.
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
++, soi = 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.