Expressions and operators · about 35 min

Assignment forms

Why this matters

So far you have bound one name at a time with name = value. Real code constantly updates values (count += 1), unpacks several results at once (quotient, remainder = divmod(a, b)), swaps values, and sometimes needs to capture a value in the middle of an expression. Python has compact, readable syntax for all of these. Each one is still just binding names to objects, and keeping that model in mind explains the one genuinely tricky case: += on a list that has another name.

Augmented assignment

Updating a name based on its own value is so common that Python has a shorthand for every arithmetic operator:

ShorthandMeans (for immutable values)
x += yx = x + y
x -= yx = x - y
x *= yx = x * y
x /= yx = x / y
x //= yx = x // y
x %= yx = x % y
x **= yx = x ** y
score = 0
score += 10
score += 5
score *= 2
print(score)
greeting = "Hello"
greeting += ", world"
print(greeting)
30
Hello, world

There is no ++ or -- in Python. x++ is a SyntaxError, and ++x is legal but means +(+x), which does nothing. Write x += 1.

Note that the name must already exist: total += 1 on a fresh name raises NameError: name 'total' is not defined, because Python must read the old value first. That is why accumulator code starts with total = 0.

What += really does: in-place where possible

For ints, floats, strings and tuples (immutable types), x += y computes a new object and rebinds x, exactly like x = x + y. But a type may define an in-place version of the operator. Lists do: += on a list extends the existing list object, then rebinds the name to that same object.

a = [1, 2]
b = a           # alias
a += [3]        # in place: mutates the shared list
print(a, b)

c = [1, 2]
d = c
c = c + [3]     # builds a new list; only c is rebound
print(c, d)
[1, 2, 3] [1, 2, 3]
[1, 2, 3] [1, 2]
after a += [3]:              after c = c + [3]:
  a --\                        c ----> [1, 2, 3]   (new object)
       >--> [1, 2, 3]          d ----> [1, 2]      (old object)
  b --/

Underneath, a += [3] compiles to a BINARY_OP instruction with the in-place flag (+=), which calls the list's __iadd__ method if it has one, and falls back to __add__ otherwise. You will meet these "dunder" methods properly in the object-oriented module. For now, remember the rule: on mutable objects, augmented assignment may mutate; on immutable ones it always rebinds.

Multiple assignment and unpacking

You can bind several names in one statement by putting comma-separated names on the left:

x, y = 3, 4
print(x, y)
q, r = divmod(17, 5)
print(q, r)
3 4
3 2

The right side 3, 4 creates a tuple (an immutable sequence; the comma makes it, not parentheses). The left side unpacks it: the first item is bound to x, the second to y. The number of names must match the number of items:

a, b = 1, 2, 3
ValueError: too many values to unpack (expected 2)

Module 7 extends this with *rest patterns for collecting leftover items.

Swapping

Because the whole right-hand side is evaluated before any name is rebound, swapping needs no temporary variable:

a = "left"
b = "right"
a, b = b, a
print(a, b)
right left

Step by step: evaluate b, a, building the tuple ("right", "left") from the objects the names refer to now. Then bind a to the first element and b to the second. Compare the naive version:

a = b      # a and b both refer to "right"; "left" has no name now
b = a      # b refers to "right" again

That loses the original value of a, which is the bug in this lesson's swap exercise starter.

Chained assignment

x = y = 0 binds both names to the same object:

x = y = 0
x += 1
print(x, y)
1 0

With an immutable 0 that is harmless: x += 1 rebinds x. With a mutable object it means aliasing: after a = b = [], appending to a also changes what b shows. To get two separate lists, write a = [] and b = [] on separate lines.

The walrus operator :=

Ordinary assignment is a statement: it cannot appear inside an expression. Python 3.8 added the assignment expression name := value, nicknamed the walrus operator because := looks like a walrus lying on its side. It binds the name and produces the value, so the value can be used right away:

print((n := 10) * 2)
print(n)
20
10

The usual purpose is to avoid computing something twice or splitting a natural expression across lines. You will see its typical uses in the next module, with if and while:

if (size := len(text)) > 80:
    print("too long by", size - 80)

while (line := input()) != "quit":
    print("you said", line)

Rules: the walrus is not allowed as a bare statement (n := 5 alone is a SyntaxError; use n = 5), and it usually needs parentheses inside larger expressions. Use it sparingly: a separate assignment line is often clearer.

Evaluation order

In an assignment, Python evaluates the right-hand side first, then binds targets left to right. Within an expression, operands are evaluated left to right. These rules make every example in this lesson predictable:

i = 0
i, x = i + 1, i + 10    # right side uses the old i (0): (1, 10)
print(i, x)
1 10

Common mistakes

Recap

Going further: run import dis; dis.dis("a, b = b, a") to see how CPython swaps (it does not even build a tuple; it uses a stack SWAP). PEP 572 explains the motivation for the walrus operator.

Exercises

Exercise 1 · quiz

Follow the updates

What does this print?

x = 10
x += 5
x //= 4
x **= 2
x -= 1
print(x)
Answer

10 + 5 = 15; 15 // 4 = 3; 3 ** 2 = 9; 9 - 1 = 8. Each x op= y rebinds x to the result of x op y. For immutable ints that is all that happens: a new int object each step.

Exercise 2 · quiz

+= on a shared list

Compare the two programs. What do they print, respectively?

# program A
a = [1]
b = a
a += [2]
print(b)
# program B
a = [1]
b = a
a = a + [2]
print(b)
Answer
  • A prints [1, 2], B prints [1]

For a mutable list, a += [2] extends the existing list in place (it calls list.__iadd__) and then rebinds a to that same object, so b, an alias of the same list, sees [1, 2]. a = a + [2] builds a new list and rebinds only a; b still refers to the old [1]. For immutable types such as ints and strings, += always produces a new object, so the two forms behave identically there.

Exercise 3 · write code

Swap without a temporary

Read two lines into a and b (keep them as strings). Swap them using a single tuple assignment, then print exactly (for inputs left and right):

a = right
b = left

The check also looks at your code: it must contain a tuple assignment that swaps a and b (no temporary variable needed).

File: main.py

Starting point

a = input()
b = input()
a = b
b = a
print("a =", a)
print("b =", b)

Example

input

left
right

expected output

a = right
b = left

Example

input

1
2

expected output

a = 2
b = 1
Hint 1

The starter loses the original a: after a = b, both names refer to the same string.

Hint 2

The right side of a, b = b, a is evaluated completely (into a tuple) before any name is rebound.

Reference solution
a = input()
b = input()
a, b = b, a
print("a =", a)
print("b =", b)

Exercise 4 · write code

Splitting a bill

Read four whole numbers, one per line: three item prices in cents and a tip in cents. Then read a fifth number, the number of people. Build up the bill with augmented assignment:

  1. start with total = 0 and add each price with +=,
  2. add the tip with +=,
  3. use divmod to split it among the people.

Print exactly (for 1250, 899, 451, 400, 3):

total: 3000
each: 1000
left over: 0

The check requires that your program uses +=.

File: main.py

Starting point

p1 = int(input())
p2 = int(input())
p3 = int(input())
tip = int(input())
people = int(input())
total = 0

Example

input

1250
899
451
400
3

expected output

total: 3000
each: 1000
left over: 0

Example

input

1000
1000
1001
0
4

expected output

total: 3001
each: 750
left over: 1
Hint 1

total += p1 means total = total + p1.

Hint 2

each, left = divmod(total, people) unpacks the quotient and remainder.

Reference solution
p1 = int(input())
p2 = int(input())
p3 = int(input())
tip = int(input())
people = int(input())
total = 0
total += p1
total += p2
total += p3
total += tip
each, left = divmod(total, people)
print("total:", total)
print("each:", each)
print("left over:", left)

Exercise 5 · quiz

Assignment as an expression

What does this print?

print((n := 4) * n, n)
Answer
  • 16 4

(n := 4) is an assignment expression: it binds n to 4 and evaluates to 4. Operands are evaluated left to right, so by the time the right-hand n of the multiplication is evaluated, n exists: 4 * 4 = 16. The final n is 4. The parentheses are required here; a bare n := 4 is not allowed as a top-level statement.