Control Flow
Python expresses branching with if/elif/else, structural matching with match/case, and iteration with for and while. Loop else clauses and short-circuit boolean operators are idioms worth learning early.
Recipe
for index, item in enumerate(["a", "b", "c"], start=1):
if item == "b":
continue
print(index, item)
match status:
case 200:
print("ok")
case 404 | 410:
print("missing")
case code if 500 <= code < 600:
print("server error")When to reach for this:
- Branching on enums, HTTP codes, or command types
- Destructuring nested JSON-like data with
match - Scanning sequences with early exit (
break) - Combining multiple iterables with
zip
Working Example
from dataclasses import dataclass
@dataclass
class Event:
kind: str
payload: dict[str, object]
def handle(event: Event) -> str:
match event:
case Event(kind="user.created",
What this demonstrates:
matchbinds variables (uid,email) while matching dict structure- Class patterns combine type and attribute constraints
for/elsereturns a sentinel when nobreakfirescontinueskips to the next iteration without exiting the loop
Deep Dive
How It Works
- Truthiness drives
if- Objects implement__bool__or__len__. - Pattern matching - Introduced in 3.10; cases are checked top to bottom; first match wins.
- Short-circuit -
a and bevaluatesbonly ifais truthy;a or bskipsbifais truthy. - Iteration protocol -
forcallsiter()then repeatednext()untilStopIteration. - while else - Same semantics as
for else- runs when loop ends withoutbreak.
match Features at a Glance
| Pattern | Example | Matches |
|---|---|---|
| Literal | case 404: | Exact value |
| OR | case 401 | 403: | Either value |
| Guard | case int(n) if n > 0: | Pattern + condition |
| Sequence | case [x, y, *rest]: | List/tuple length and shape |
| Mapping | case {"id": int(i)}: | Dict keys and types |
Python Notes
# walrus := assigns inside expressions (3.8+)
if (line := input()) == "quit":
pass
# zip strict=True (3.10+) raises on unequal lengths
for a, b in zip(xs, ys, strict=True):
...Gotchas
- Missing break in while True - Infinite loops hang workers. Fix: Explicit break condition or timeout.
- match fall-through - No automatic fall-through between cases (unlike C switch). Fix: Combine with
|or shared logic. - Loop else confusion -
elseruns when nobreak, not on falsy condition. Fix: Comment intent or refactor to functions. - Modifying list while iterating - Skips elements or raises errors. Fix: Iterate a copy or build a new list.
- Truthy empty containers -
[0]is truthy because non-empty. Fix: Compare lengths or values explicitly when needed.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
if/elif chain | Simple scalar comparisons | Deeply nested JSON shapes |
match/case | Structural destructuring | Two-branch boolean only |
| Dict dispatch | Many callables keyed by str | Need destructuring |
filter/map | Functional transforms | Side effects per item |
FAQs
When should I use match instead of if?
When branching on structure - typed dict keys, tuple arity, or class attributes. Stick to if for simple booleans.
What does for-else mean?
The else block runs after the loop completes without hitting break. Useful for search loops.
How do I exit nested loops?
Use a flag, extract to a function with return, or in 3.11+ structured exception patterns sparingly.
Does match use == or isinstance?
Class patterns use isinstance semantics; literals use equality. Sequence patterns match structure and length.
What is short-circuit evaluation?
and/or stop evaluating once the result is determined - use for guard clauses like obj and obj.method().
How do I loop with an index?
enumerate(items, start=0) - cleaner than manual counter variables.
Can I match on custom classes?
Yes - case MyClass(attr=value): matches instances with attributes. Dataclasses work well here.
What is continue vs pass?
continue jumps to next iteration. pass is a no-op placeholder in syntax-required blocks.
How do I handle optional values in match?
Include case None: or use guards. Combine with type hints and narrowing in static checkers.
Is while else common?
Rare in application code. for/else for search is more idiomatic; prefer functions over clever loop else.
Related
- Comprehensions & Generator Expressions - loop-free iteration
- Functions, Args & Scope - early return instead of deep nesting
- Enums - match on enum members
- Dunder / Magic Methods - customizing truthiness
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.