Walrus Operator (:=)
Concepts covered: Variables
name := expression evaluates the expression, binds the result to name, and hands the same value to whatever surrounds it. Plain = is a statement and cannot sit inside a larger expression. :=, nicknamed the walrus operator for its eyes and tusks, is an expression. It arrived in Python 3.8, so any 3.8 or later interpreter accepts it.
print((n := 7) * 2)
print(n)Output:
14 7
Parentheses decide what gets bound
:= binds more loosely than every other operator, so the comparisons and even and and or are evaluated before it. Inside the parentheses, count := 3 > 2 binds count to the result of 3 > 2, which is the bool True, and the 3 itself is never stored. Wrap the walrus around exactly the value you want to keep.
ok = (count := 3) > 2
print(ok, count)
ok = (count := 3 > 2)
print(ok, count)Output:
True 3 True True
Compute once, test, then reuse
The walrus pays off where an expression needs a value twice: once to test it and once to report it. Paired with a short-circuit guard, it computes the average row width only after the zero check passes, and keeps the result for the next line.
total_bytes = 7340032
row_count = 2048
too_wide = row_count != 0 and (avg := total_bytes / row_count) > 1024
print(too_wide, avg)Output:
True 3584.0
The binding happens only if Python reaches the walrus. Set row_count to 0 and and stops at its left operand, so the name avg is never created and reading it raises NameError.
total_bytes = 7340032
row_count = 0
too_wide = row_count != 0 and (avg := total_bytes / row_count) > 1024
print(too_wide)
try:
print(avg)
except NameError as err:
print("NameError:", err)Output:
False NameError: name 'avg' is not defined
A guard over a stream of readings
The animation pulls 3 readings from a generator and checks each on a single line. The walrus captures the reading, the identity test lets the real zero through as a reading, and the comparison runs only when the feed produced a number.
def readings():
yield 12.5
yield 0.0
yield None
stream = readings()
ok1 = (r1 := next(stream)) is not None and r1 > 10
ok2 = (r2 := next(stream)) is not None and r2 > 10
ok3 = (r3 := next(stream)) is not None and r3 > 10
print(ok1, ok2, ok3)
print(r1, r2, r3)
print(r1 and r1 > 10, r2 and r2 > 10)True False False 12.5 0.0 None True 0.0
The last print shows why the identity test matters. With truthiness in its place, the reading of 0.0 short-circuits to the float 0.0, and a report treating that value as a check result would mix a measurement into a yes or no column.
- Do
- Wrap every walrus in parentheses around the exact value to keep
- Bind the value where it is first needed, then reuse the name
- Read a name bound inside and or or only on the path that bound it
- Avoid
- Pack several walruses into a line nobody can scan
- Use := where an ordinary = statement on its own line reads fine
- Read a walrus name after a short-circuit that may have skipped it
What does this print?
row_count = 4
ok = (wide := 5000 / row_count > 1000) and row_count > 2
print(ok, wide)- True True
- True 1250.0
- 1250.0 True
- NameError for wide
Capstone: one event, every operator
The last 2 problems put the whole lesson into a single pipeline step. It decodes a message event's ID and tests a permission bit before guarding a division; after that it reads present values and compares float results.
A pipeline step like this one usually breaks through a small edit, and most such edits are one of 3 slips: a guard moved to the right of the division it protects, so the division runs first; a revoke written with |, which can only turn bits on, where & ~ was needed; and a float result checked with == after it took a different arithmetic path from its target. The next program contains one of each.
About this lesson
This section is part of the Python Expressions: Advanced lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.