Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner Python 3.8+ ~26 min· topic 9 of 9

Topic 2.9

The Walrus Operator :=

In one line

The walrus operator := assigns a value to a name inside an expression, so you can test a value and keep it in one step. It shortens loops that read until a stop signal and filters that need a computed value twice, and it is not worth using when it makes a line harder to read.

Think of it like this

A cashier scanning an item. She reads the price and, in the same moment, writes it on the receipt. She does not scan the item twice, once to look and once to write. The walrus operator is that: compute a value, test it, and keep it, all in one go. (Its name comes from the := shape, which looks like a walrus lying on its side, eyes and tusks.)

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Walrus operator
The symbol :=. It assigns a value to a name inside an expression and gives that value back.
Assignment statement
A line like x = 5 that stores a value. It cannot be used inside another expression.
Assignment expression
A piece like (x := 5) that stores a value and also is that value.
Loop-and-a-half
A loop that reads a value in the middle and decides to stop, so the read and the test belong together.
Scope
Where a name exists. A walrus name belongs to the surrounding function or module, not just the expression.
Side effect
A change a piece of code makes besides giving a value, such as pop(0) removing an item.
pop(0) (preview)
A list method that removes the first item and returns it. Phase 4 covers list methods.

Step by step

01The problem: two steps for one idea

Both snippets do the same job. The first computes the length on its own line and the second lets you write it inside the condition. The result n is available in the block either way.

main.pywhole filepython
text = "supercalifragilistic"

# Without the walrus
n = len(text)
if n > 10:
    print(f"{n} characters is too long")

# With the walrus
if (n := len(text)) > 10:
    print(f"{n} characters is too long")

02Loop-and-a-half

inbox.pop(0) takes the first message out of the list and gives it back. The walrus stores it in message and the loop tests it against "stop". After the loop, the list still holds what was never read: "ignored".

main.pywhole filepython
inbox = ["hello", "world", "stop", "ignored"]
while (message := inbox.pop(0)) != "stop":
    print("got", message)
print("left in inbox:", inbox)
Loop-and-a-halfdiagram
Rendering diagram…

03Keep a lookup result

A dictionary's get returns None when a key is missing (Topic 4.4). The walrus stores the result and the test is not None checks it, with no second lookup. After the test, the block can use price directly.

main.pywhole filepython
prices = {"apple": 30, "banana": 12}
if (price := prices.get("apple")) is not None:
    print("apple costs", price)

04Reuse a value inside a comprehension

A comprehension builds a list in one line (Topic 4.7). The condition squares each x once, keeps the result in sq, tests it, and the part before for collects the same sq. Without := you would compute x * x twice.

main.pywhole filepython
data = [3, 8, 1, 9, 4]
big_squares = [sq for x in data if (sq := x * x) > 10]
print(big_squares)        # [64, 81, 16]

05The name stays alive

The walrus name is not private to the if. After the second check, price still exists and holds None. This is useful but can surprise you when you reuse a name by accident.

main.pywhole filepython
prices = {"apple": 30}
if (price := prices.get("kiwi")) is not None:
    print("kiwi costs", price)
else:
    print("no kiwi")
print("price is now", price)       # price is now None

06What is not allowed, and the brackets habit

A walrus cannot be a whole statement. Use = for that. Inside a larger expression, put the walrus in brackets, as in y = (z := 5) + 1: z becomes 5 and y becomes 6.

main.pywhole filepython
# y := 5            # SyntaxError: invalid syntax
y = (z := 5) + 1
print(y, z)           # 6 5

07When not to use it

If the plain version is just as short, keep it. If you need two walruses in one line or the reader must pause to decode it, split it up. The walrus is a tool for removing repetition, not a style goal.

In this sketch, compute() stands for any function (Phase 5) and ... means "more code here". It is not meant to be run.

main.pywhole filepython
# Harder to read than it needs to be
if (a := compute()) and (b := a * 2) > 10:
    ...

# Clearer
a = compute()
b = a * 2
if a and b > 10:
    ...

Try it yourself

  1. 1

    Remove the walrus

    Rewrite the while loop in the first example without :=. You will need either a duplicated read or a while True with a break. Compare the lengths.

  2. 2

    Filter with a stored value

    Change the comprehension to keep only the squares that are greater than 20. Predict the list, then run (it is [64, 81]).

  3. 3

    See the leak

    After the comprehension in the second example, try print(sq). The name sq is available outside: it holds the last value that was computed.

Code & diagrams

Walrus in a while loop and an if Python 3.8+ New tab

Move "stop" to the front of the list and run it: the loop body never runs. Change the 10 to 25 and watch the if branch switch off.

Sign in to run this example in your browser.

Expected output

got hello
got world
left in inbox: ['ignored']
20 characters is too long
Comprehension reuse and the lasting name Python 3.8+ New tab

Check the line "price is now None": the name from the last test is still there. Then try y = (z := 5) + 1 with other numbers.

Sign in to run this example in your browser.

Expected output

[64, 81, 16]
apple costs 30
no kiwi
price is now None
6 5

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Walrus as a whole statement

Use := where a plain statement is expected.

main.pywhole filepython
y := 5
print(y)
terminal
$ python main.py
── what you'll see ──
File "main.py", line 1
y := 5
^^
SyntaxError: invalid syntax

Myth vs fact

Myth

:= is just another way to write =.

Fact

= is a statement. := is an expression that returns the value, so it can be used inside conditions and comprehensions.

Myth

The walrus name only exists inside the if or the loop.

Fact

It is bound in the surrounding scope and stays after the statement ends.

Myth

More walruses make code more Pythonic.

Fact

It is a tool for removing duplication. Overusing it makes code harder to read.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Assignment expressions came from PEP 572 and arrived in Python 3.8 after a long debate. The need was real: while chunk := file.read(8192): is now the standard way to read a file in blocks.

  • ▸

    In a comprehension, a walrus binds the name in the containing function, not in the comprehension's own scope, which is deliberately different from the loop variable. You also cannot rebind the comprehension's own iteration variable with it.

  • ▸

    any() and all() with a walrus can capture the item that made them stop, a neat trick for "find and keep" in one line: if any((hit := x) > 10 for x in data): print(hit). Use it only when the code stays readable.

  • ▸

    Precedence rules to know: := binds more loosely than everything else, so (x := 1, 2) is the tuple (1, 2) with x = 1. It is not allowed unparenthesised at the top of an expression statement, as a keyword-argument value (f(a=x := 1)), or in the iterable part of a comprehension ([y for y in (z := data)] is a SyntaxError). Inside a class body a comprehension cannot use it at all.

  • ▸

    Linters (Ruff) and teams often allow the walrus in if and while heads and discourage it elsewhere. Watch the Python 3.8 floor: a library that supports 3.7 cannot use it.

Remember this

  1. 1

    You already know the assignment statement x = 5. Recall from Topic 2.2: a statement does something, an expression gives a value. x = 5 stores a value but is not an expression, so it cannot sit inside a condition. The assignment expression x := 5 does both jobs: it stores the value and the whole expression *has* that value. So (n := len(text)) > 10 stores the length in n and also tests it.

  2. 2

    The classic use is to avoid computing something twice. Without it you write n = len(text) on one line and if n > 10: on the next, or you call len(text) twice. With it, if (n := len(text)) > 10: computes once, tests, and leaves n ready for the block: print(f"{n} characters is too long").

  3. 3

    The second classic use is the loop-and-a-half: read a value, and stop when it is a stop signal. while (message := inbox.pop(0)) != "stop": takes the next message (pop(0) removes and returns the first item of a list) and loops while it isn't "stop", and message is available in the body. The old way needed the read written twice, once before the loop and once at the end of the body, or a while True with a break.

  4. 4

    It also helps when a comprehension must test a computed value and keep it: [sq for x in data if (sq := x * x) > 10] squares each number once, filters on it, and collects the same value. A comprehension is a one-line way to build a list (Topic 4.7). Without the walrus the square is calculated twice.

  5. 5

    Know the rules. A bare y := 5 as a whole statement is a SyntaxError; use y = 5. The expression usually needs brackets (if (n := 5) > 3), and putting them in always is a safe habit. The name belongs to the surrounding code, not just to the if. (Its scope, the place where the name exists, is the whole function or file.) So it stays alive after an if or loop, and even after a comprehension. After the test if (price := prices.get("kiwi")) is not None, price is still there, holding None. (prices.get(...) looks up a key and gives None if it is missing; Topic 4.4.)

  6. 6

    Use it sparingly. It is worth it when it removes a repeated expensive call or a duplicated read, and when the line stays easy to read. If a reader has to stop and decode it, a plain assignment on its own line is better. Nested or multiple walruses on one line are almost always a mistake.

Explain it without notes

01

What does the walrus operator do, and how is it different from =?

02

Show a loop that reads until a stop value, with and without :=.

03

Why does a name set with := inside an if still exist after the if?

04

When should you avoid the walrus operator?

Practice

01

With inbox = ["a", "b", "end", "c"], use a walrus loop with pop(0) to print each message until "end". Print a then b.

02

Use if (n := len("hello world")) > 5: to print Long text: 11.

03

Using a comprehension with a walrus, collect the squares greater than 20 from [2, 5, 3, 6] and print [25, 36].

Trade-offs

  • ↔

    Fewer lines vs readability. The walrus removes duplication but packs more into a line. Use it where the result still reads naturally.

  • ↔

    Compatibility. Code that uses := needs Python 3.8 or newer. Old environments will refuse to run it.

Done when you can

  • Done when you can explain the difference between = and :=.

  • Done when you can write a loop-and-a-half with := and an if that keeps a computed value.

  • Done when you can say why the walrus name stays alive after the statement.

  • Done when you can name the cases where a plain assignment is the better choice.