Control flow#

Control flow is how the operator’s program decides what runs next. Python keeps the surface small: if / elif / else for branching, for and while for iteration, match / case for destructuring, and break / continue / return / pass / raise for jumps.

This page is the day-to-day reference for the control-flow constructs in the language. For the values they branch on, see Types. For functions and the patterns they enable (early return, recursion, generators), see Functions. For the exception flow raise participates in, see Errors.

Conditional#

if / elif / else evaluates branches top-to-bottom and takes the first that matches. elif is sugar for else: if.

        flowchart TD
    A([start]) --> B{x > 0?}
    B -->|true| C[positive]
    B -->|false| D{x == 0?}
    D -->|true| E[zero]
    D -->|false| F[negative]
    C --> Z([end])
    E --> Z
    F --> Z
    
if x > 0:
    positive()
elif x == 0:
    zero()
else:
    negative()

Conditional expressions (the ternary form) inline a branch as an expression.

z = a if cond else b

Useful inside list-comprehensions or assignments where a full if statement would be heavy.

label = "even" if n % 2 == 0 else "odd"

Truthiness rules (False, None, 0, 0.0, empty containers, anything whose __bool__ returns False) determine what counts as true in a condition. See Types for the full table.

Idioms#

Empty check; reach for the truthy form, not == [] or len(xs) == 0.

if not items:
    return

Membership against a small fixed set, cleaner than a chain of == comparisons.

if x in {1, 2, 3}:
    handle(x)

Type test with isinstance; accepts a tuple of types.

if isinstance(x, (int, float)):
    handle_number(x)

Bind-and-test with the walrus := to avoid recomputing or re-fetching the value.

if (n := len(payload)) > MAX_SIZE:
    raise ValueError(f"payload too large: {n} bytes")

Loops#

for iterates anything implementing __iter__; while loops while the condition is truthy. Both accept an optional else clause that runs only if the loop exits without break.

For loop#

        flowchart TD
    A([start]) --> B{more in iterable?}
    B -->|yes| C[bind item]
    C --> D[body]
    D --> B
    B -->|no| E([end])
    

Iterate anything implementing __iter__; the standard form the operator reaches for first.

for item in iterable:
    handle(item)

Index and value at once with enumerate.

for i, item in enumerate(xs, start=1):
    print(i, item)

Parallel iteration with zip; stops at the shortest.

for a, b in zip(xs, ys):
    combine(a, b)

Walk a mapping’s key-value pairs.

for k, v in mapping.items():
    store(k, v)

Walk a sequence in reverse without materialising a copy.

for x in reversed(xs):
    handle(x)

File iteration is line-by-line; the file object itself is the iterable.

for line in open("log.txt"):
    parse(line)

Cross-products, permutations, and adjacent pairs come from itertools.

from itertools import product, permutations, combinations, pairwise
for a, b in pairwise(xs):
    diff(a, b)

Iterate keys in a fixed order without sorting in place.

for k in sorted(d):
    handle(k, d[k])

Idiomatic loop forms at a glance.

Form

When

for x in xs

Direct iteration; never for i in range(len(xs)).

for i, x in enumerate(xs)

Index and value at once.

for a, b in zip(xs, ys)

Parallel iteration; stops at the shortest.

for k, v in d.items()

Walk a mapping’s key-value pairs.

for k in sorted(d)

Walk keys in sorted order.

itertools.product / permutations / combinations

Cross-products and orderings.

itertools.pairwise(xs)

Adjacent pairs (3.10+).

While loop#

        flowchart TD
    A([start]) --> B{cond truthy?}
    B -->|yes| C[body]
    C --> B
    B -->|no| D[else clause]
    D --> E([end])
    

Standard form with an optional else clause that runs only when the loop exits without break.

while cond:
    step()
else:
    cleanup()

Walrus := in the condition binds and tests in one go; avoids the read-then-check duplication.

while (chunk := f.read(4096)):
    process(chunk)

Loop-else#

The else clause on a loop runs only if the loop exits without break. Useful for search-then-fallback patterns.

for x in xs:
    if found(x):
        break                # else clause is skipped
else:
    not_found()              # runs only if no break fired

Match-case#

match / case (Python 3.10+) destructures values against patterns. Each case is tried top-to-bottom; the first match wins. _ is the wildcard.

        flowchart TD
    A([match subject]) --> B{case 1 matches?}
    B -->|yes| C[case 1 body]
    B -->|no| D{case 2 matches?}
    D -->|yes| E[case 2 body]
    D -->|no| F{...}
    F --> G[case _ body]
    C --> Z([end])
    E --> Z
    G --> Z
    
match command.split():
    case ["go", direction]:
        move(direction)
    case ["set", *args] if args:
        configure(args)
    case {"type": "click", "pos": (x, y)}:
        click(x, y)
    case Point(x=0, y=0):
        at_origin()
    case _:
        unknown()

Pattern kinds you should know.

Pattern

Matches

literal

exact value: 42, "hello", None, True

name

any value; binds the name (note: Point matches only when it is dotted, e.g. shapes.Point)

_

any value; binds nothing (wildcard)

[a, b, *rest]

sequence with at least two elements; binds rest

{"key": val}

mapping with "key"; binds val

Point(x=0, y=0)

instance with matching attributes

a | b

either pattern matches (alternation)

pat if guard

pattern with a boolean guard

Jumps#

The five primitives that interrupt the natural top-to-bottom flow of a block.

Keyword

Effect

break

Exit the innermost for or while; skips its else.

continue

Skip the rest of the loop body, jump to the next iteration.

return

Exit the current function with a value (default None).

pass

No-op. Use as a placeholder when a block is required but the operator has nothing to put in it yet.

raise

Throw an exception (see Errors).

Early return on success, explicit fall-through return for the no-match case.

def first_match(items, pred):
    for x in items:
        if pred(x):
            return x
    return None

continue and break together model a scan-with-skip loop.

for line in lines:
    if line.startswith("#"):
        continue
    if line.strip() == "STOP":
        break
    process(line)

return from a generator (def with yield) raises StopIteration with the return value as its argument. pass in an if body or class body is the operator’s “TODO” marker that still parses.

References#

  • Syntax for the lexical surface if / for / while / match live in.

  • Types for truthiness rules and what counts as “true” in a condition.

  • Functions for return, generators, and comprehensions as expression-form loops.

  • Errors for raise and the exception-driven flow patterns.

  • PEP 634, structural pattern matching.