The walrus, ternary chaining, and small flow tricks
~18 min · walrus, assignment-expression, short-circuit, or-default
Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
The walrus operator (3.8+) — :=
The walrus assigns and evaluates in the same expression. if (n := len(items)) > 10: binds n and tests the result. The use cases: read-and-test inside if/while; capture an intermediate value inside a comprehension; avoid recomputing inside a chain. It's a small feature with surprisingly wide reach once you start noticing the pattern.
or as default, and as guard
x or default returns x if it's truthy, otherwise default. Useful: name = user_input or "anonymous". Watch out: this treats any falsy value the same. If 0 or "" is a legitimate user input and you only want to fall back when the value is missing, use x if x is not None else default or check explicitly.
Multiple return paths vs. single return
Some style guides preach "one return per function." Python doesn't. Early returns to handle edge cases up front (the "guard clause" pattern) usually read cleaner than nesting everything inside ifs. Save the deep indentation for when the logic actually requires it.
pass vs. ... vs. raise NotImplementedError
Three ways to write a placeholder: pass (do nothing, statement-level), ... (the Ellipsis literal, accepted as a statement, idiomatic in stub files and abstract methods), raise NotImplementedError (signal that a concrete subclass must override). The choice tells the reader your intent: a no-op, a stub, or an unfinished method.
Pythonic Way: When a function checks "not this, not that, not the other" and only proceeds if all are clean — write the negative checks as early returns. The remaining code lives in the happy path with no indentation. This is the "guard clause" pattern, and it's quietly the most readable shape for validation-heavy code.
Code
Walrus — read-and-test in if / while·python
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Without walrus
n = len(items)
if n > 5:
print(f"big list ({n})")
# With walrus — single line
if (n := len(items)) > 5:
print(f"big list ({n})")
# Inside a while — read each chunk
import io
stream = io.BytesIO(b"hello world this is some data")
while chunk := stream.read(8):
print(chunk)
# b'hello wo'
# b'rld this'
# b' is some'
# b' data'
Walrus inside a comprehension·python
values = [10, 20, 30, 40, 50]
# Without walrus — recompute the expensive part each time
result = [v*v for v in values if v*v > 500]
# With walrus — compute once, name the result
result = [sq for v in values if (sq := v*v) > 500]
print(result) # [900, 1600, 2500]
or-default and the falsy gotcha·python
# or-default is great when 0/empty IS missing
user_input = ""
name = user_input or "anonymous"
print(name) # 'anonymous'
# But if 0 is a valid value, or-default is wrong
def get_quantity(x):
return x or 1 # WRONG: 0 turns into 1
print(get_quantity(0)) # 1 <- bug!
print(get_quantity(None)) # 1
# Right: explicit None check
def get_quantity_v2(x):
return x if x is not None else 1
print(get_quantity_v2(0)) # 0
print(get_quantity_v2(None)) # 1
Guard clauses — flatten the indentation·python
# Nested ifs — hard to read
def process_v1(user, msg):
if user is not None:
if user.is_active:
if msg:
if len(msg) <= 280:
return f"sending: {msg}"
else:
return "too long"
else:
return "empty"
else:
return "inactive"
else:
return "no user"
# Guard clauses — flat happy path
def process_v2(user, msg):
if user is None:
return "no user"
if not user.is_active:
return "inactive"
if not msg:
return "empty"
if len(msg) > 280:
return "too long"
return f"sending: {msg}"
pass / ... / NotImplementedError — three placeholders·python
# 1. pass — explicit no-op
def nothing():
pass
# 2. ... — Ellipsis as a stub. Common in .pyi stub files and abstract methods.
def stub():
...
# 3. NotImplementedError — for abstract methods that subclasses MUST override
class Base:
def required(self):
raise NotImplementedError("subclass must override")
Write fetch_or_compute(cache, key, compute_fn) that returns cache[key] if it's there, otherwise calls compute_fn(), stores the result in cache[key], and returns it. Use the walrus operator at least once. Then write a guard-clause version of validate_email(s) that returns "invalid" for any of: None, empty string, no @, more than one @, no . after the @. Otherwise returns "ok". Test both with at least four inputs each.
Progress
Progress is local-only — sign in to sync across devices.