Walrus Operator Patterns
The walrus operator `:=` (Python 3.8+) lets you assign and use a value in the same expression. It is the right tool for caching expensive expressions inside comprehensions, reading until a sentinel, and tightening the common 'compute, then check' pattern. This snippet covers the loop-with-sentinel use, the comprehension-cache use, and the regex-match conditional that is the most-cited textbook example.
605 views
11
import io
stream = io.StringIO('line one\nline two\nline three\n')
lines = []
while (chunk := stream.readline()):
lines.append(chunk.rstrip())
print(lines) # ['line one', 'line two', 'line three']
# Without walrus, you'd duplicate the call:
# chunk = stream.readline()
# while chunk:
# lines.append(chunk.rstrip())
# chunk = stream.readline()The walrus operator collapses the classic 'read once, check, read again' pattern into a single line: while (chunk := stream.readline()):. The expression evaluates to the assigned value, so an empty string (the EOF sentinel from readline) makes the loop exit naturally. This is the cleanest pattern for streams, generators with sentinels, and any iterator-like API that does not implement the iterator protocol. The parentheses around the walrus expression are required when it is the entire condition.
def parse_amount(s):
return int(s) if s.isdigit() else None
rows = ['12', 'abc', '7', 'NaN', '99']
parsed = [n for s in rows if (n := parse_amount(s)) is not None]
print(parsed) # [12, 7, 99]Without the walrus operator, you would call parse_amount(s) twice in this comprehension: once to filter and once to project. The walrus assigns the result to n inside the predicate, then the output expression n reuses it. This pattern is everywhere in real code: regex matches, type conversions, dictionary lookups. The trade-off is reduced readability for simple cases; reach for it when the expression on the left is genuinely expensive or has side effects.
import re
patterns = [r'^([A-Z]+)-(\d+)$', r'^(\d{4})-(\d{2})-(\d{2})$']
lines = ['ABC-123', '2024-01-15', 'noise', 'XY-99']
for line in lines:
if (m := re.match(patterns[0], line)):
print(f'ID match: {m.group(1)} number {m.group(2)}')
elif (m := re.match(patterns[1], line)):
print(f'Date match: {m.group(0)}')
else:
print(f'No match: {line}')Before the walrus operator, the canonical regex match required either a separate m = re.match(...) line before each if, or an outer assignment that polluted scope unnecessarily. With if (m := re.match(...)), the assignment is bound only when the branch is taken, and you can use m.group(...) inside the body without wondering whether m was set. Chaining multiple patterns with elif makes the dispatch read like a parser. This is the example most PEP-572 advocates lead with, and for good reason.
