The walrus operator (:=), formally an assignment expression, was introduced by PEP 572 in Python 3.8. Unlike the = statement, := is an expression that both assigns to a name and evaluates to the assigned value, so it can be used inside conditions, comprehensions, and other expressions.
Assign inside an expression
What you will learn: how := assigns a value as part of a larger expression, letting you capture and test a value in one step.
How to read this tab: Remember := needs parentheses in most places, and cannot replace = at the top level.
Normally = is a statement on its own line. The walrus := lets you assign a value inside a bigger expression — for example, computing a value and testing it in the same if. The name comes from its resemblance to a walrus's eyes and tusks.
Walrus needs parentheses, and cannot replace = at top level. Write if (n := len(x)) > 10: with parentheses. A bare n := 5 on its own line is a syntax error — use normal = there. The walrus is only for when you are inside a larger expression.
Assigning inside an expression
The walrus operator captures a value into a variable at the same moment you use that value. This removes the common pattern of computing something, then immediately testing it on the next line.
# WITHOUT walrus — compute, then test on separate lines
name = input("Name: ")
if len(name) > 50:
print("Too long")
# WITH walrus — assign and test in one expression
if (length := len(name)) > 50:
print(f"Too long: {length} chars")
# 'length' is now available for use afterwards too
# Useful when you need the value AND the test
import re
text = "order #12345"
if (match := re.search(r"#(\d+)", text)):
print(f"Order number: {match.group(1)}") # 12345
# Without walrus you'd call re.search twice or use a temp variable
# Avoid recomputing an expensive value
data = [1, 2, 3, 4, 5]
if (total := sum(data)) > 10:
print(f"Sum {total} exceeds limit") # reuses total, no recomputeTo avoid ambiguity, := usually must be wrapped in parentheses: if (n := len(x)) > 10:. A bare := at statement level (n := 5 on its own line) is a syntax error — use the normal = there. The walrus is for when you are inside a larger expression.
✅ Beginner tab complete
- I can use := to assign and test in one if statement
- I know := requires parentheses in most contexts
- I know := is an expression while = is a statement
- I know a bare := at top level is a syntax error
while loops and comprehensions
What you will learn: the classic while-loop read pattern, and using := in comprehensions to compute a value once instead of twice.
How to read this tab: The file-chunk-reading while loop is the canonical walrus example — learn it.
The canonical pattern: read once, in the condition. while (chunk := f.read(8192)): reads until empty, with no duplicated read call before and inside the loop. This is the example PEP 572 was largely designed for.
The classic use: while loops reading input
The walrus operator's most celebrated use is collapsing the "read, test, process, read again" loop pattern into a single clean line — eliminating the duplicated read call.
# WITHOUT walrus — the read appears twice (before loop AND inside)
line = input("> ")
while line != "quit":
print(f"You said: {line}")
line = input("> ") # duplicated read — easy to forget
# WITH walrus — read once, in the condition
while (line := input("> ")) != "quit":
print(f"You said: {line}")
# Reading a file in fixed-size chunks — a canonical example
with open("big.bin", "rb") as f:
while (chunk := f.read(8192)): # read until empty bytes
process(chunk)
# Consuming from a queue until empty
import queue
q = queue.Queue()
# ... fill q ...
while not q.empty():
item = q.get()
process(item)Walrus avoids double computation in comprehensions. [y for x in data if (y := f(x)) > 0] calls f once per item; the naive version [f(x) for x in data if f(x) > 0] calls it twice. But remember the walrus name escapes into the enclosing scope.
In comprehensions — compute once, use twice
Inside a comprehension, the walrus lets you compute a value once and use it both in the filter condition and the output expression — avoiding a double computation.
def expensive(x):
# imagine this is slow
return x * x - 10
# WITHOUT walrus — expensive() called TWICE per item
result = [expensive(x) for x in range(10) if expensive(x) > 0]
# WITH walrus — expensive() called ONCE per item
result = [y for x in range(10) if (y := expensive(x)) > 0]
print(result) # [6, 15, 26, 39, 54, 71]
# Capture intermediate values while filtering
data = ["1,2", "3,4", "bad", "5,6"]
parsed = [
(a, b)
for item in data
if len(parts := item.split(",")) == 2
and parts[0].isdigit()
for a, b in [parts]
]
# Walrus scope in comprehensions LEAKS to the enclosing scope
# (unlike the loop variable, which does not)
nums = [y := x + 1 for x in range(3)]
print(y) # 3 — y escaped the comprehension (last assigned value)= is a statement; it cannot appear inside an if or comprehension. := is an expression; it can. You cannot use := as a plain top-level assignment (x := 5 alone is a syntax error) — use = there.:= inside a comprehension binds in the enclosing scope. This is intentional (PEP 572) but surprising.✅ Intermediate tab complete
- I can write while (line := input()) != "quit"
- I can read file chunks with while (chunk := f.read(n))
- I can use := in a comprehension to avoid computing a value twice
- I know walrus names leak from comprehensions into the enclosing scope
Scope rules and prohibited forms
What you will learn: the comprehension-scope exception, the forms PEP 572 prohibits, and the readability guidance.
How to read this tab: Read for the precise rules — especially that walrus binds to the containing scope, not the comprehension.
Scoping rules, prohibited forms, and readability
PEP 572 specifies precise rules for assignment expressions. The bound name belongs to the containing scope, not any comprehension it appears in — the one deliberate exception to the rule that comprehensions have their own scope. Several forms are explicitly prohibited to prevent confusing or redundant code: you cannot use := at the top level of an expression statement, cannot assign to attributes or subscripts (obj.x := 1 is illegal), and cannot mix it with = on the same target.
# Comprehension scope exception — y binds OUTSIDE the comprehension
total = 0
values = [total := total + x for x in range(5)]
print(values) # [0, 1, 3, 6, 10] — running total
print(total) # 10 — the walrus name persists in enclosing scope
# Prohibited forms (all SyntaxError):
# x := 5 # bare top-level — use x = 5
# obj.attr := 5 # cannot target an attribute
# d["key"] := 5 # cannot target a subscript
# x = (y := 5) = 3 # cannot chain with =
# Legitimate advanced use: avoid redundant work in any() / all()
lines = ["", " ", "hello", "world"]
# Find first non-blank line AND keep it
if any((stripped := ln.strip()) for ln in lines):
pass # 'stripped' holds the LAST evaluated value (short-circuit aware)
# Use in f-strings for debugging (3.8+) combined with walrus
import math
r = 5
print(f"{(area := math.pi * r * r) = :.2f}") # area = 78.54
print(area) # 78.539... — still availableThe PEP's stated motivation is to reduce a specific, common redundancy — computing a value, naming it, and immediately testing it — without encouraging dense one-liners. The style guidance from the PEP authors is explicit: use the walrus where it genuinely removes duplication or a throwaway temporary, and avoid it where a plain assignment on the preceding line would read more clearly. Overuse produces expressions that are hard to scan, which defeats the purpose.
✅ Expert tab complete
- I know walrus binds in the containing scope, even inside a comprehension
- I know obj.attr := and d[key] := are illegal
- I use walrus only where it removes genuine duplication