I practiced Python control flow across conditionals, loops, and the structural match statement, then refactored nested loops into comprehensions. Comprehension-based code was both more readable and measurably faster than the equivalent explicit loops.

Objective & Context

Control flow is where most logic bugs live. This lab builds idiomatic patterns, early returns, comprehension filtering, and match for dispatch, that keep automation code flat and testable instead of deeply nested.

Learning Objectives

  • Filter and transform sequences with list and dict comprehensions.
  • Dispatch on values cleanly with the structural match statement (Python 3.10+).
  • Replace nested loops with comprehensions without losing readability.
  • Benchmark alternatives with timeit to ground style choices in data.
  • Choose the control-flow construct that best fits each situation.

Environment & Prerequisites

Prerequisites

  • Python 3.11 in an activated venv (structural pattern matching needs 3.10+)
  • A script file for the match dispatch example
  • timeit from the standard library for micro-benchmarks
  • Assumed skill: basic Python syntax, lists, and functions

Step-by-Step Execution

1. Filter with a comprehension

python -c "print([n*n for n in range(10) if n%2==0])"

Expected output:

[0, 4, 16, 36, 64]

What this teaches: The comprehension expresses filter plus transform in one readable line; the equivalent nested loop needs four lines and a temporary list.

2. Dispatch with match

def start(): return "service started"
def stop(): return "service stopped"
def usage(): return "usage: cmd {start|stop}"

def route(cmd):
    match cmd:
        case "start": return start()
        case "stop":  return stop()
        case _:       return usage()

Expected output:

(no output; route is defined)

What this teaches: match reads as a dispatch table: each case names a pattern, and the wildcard case _ is the explicit default, which is clearer than chained if/elif for command routing. The start/stop/usage helpers are placeholders; replace them with real logic in your own dispatcher.

3. Benchmark loop vs comprehension

python -m timeit "[x for x in range(1000)]"

Expected output:

20000 loops, best of 5: 18.2 usec per loop

What this teaches: timeit runs the snippet thousands of times and reports the best, smoothing out system noise so the comparison between loop styles is trustworthy. Exact numbers vary by machine; run the same command with an explicit loop version to compare.

How It Works

How It Works

Comprehensions are not just shorter loops. They run in C-optimized bytecode with the loop overhead pushed into the interpreter, which is why they usually beat an equivalent for loop that appends, as the benchmark here shows.

The match statement does structural pattern matching, not just equality. Cases can destructure sequences and mappings, bind names, and add guard clauses, which makes it a compact dispatcher for commands, message types, or AST nodes.

Readability is the real criterion. A comprehension that fits on one line is clearer than a loop; a comprehension with three nested conditions is not. The refactor rule of thumb: extract the inner logic into a named function, then the comprehension stays legible.

Validation & Testing

Compare outputs and timing of the explicit-loop and comprehension versions of the same task. Pass criteria: identical results, comprehension is faster, and the match dispatcher covers every case including the default.

Advanced: Troubleshooting
  • Off-by-one: remember range is half-open; the stop value is excluded.
  • Unreachable case: order match cases from specific to general.
  • Comprehension too complex: if it needs comments, use an explicit loop.

Troubleshooting

SymptomLikely causeFix
match case never firespatterns match structure, and a literal like 200 matches only the int 200, not the string '200'check the type of the subject first; patterns do not coerce
comprehension is slower than expectedthe expression inside calls a heavy function per elementthe loop overhead is rarely the bottleneck; profile the inner expression
NameError inside a comprehensioncomprehensions have their own scope in Python 3the loop variable does not leak out; bind results to a name instead
match falls through silentlyno case matched and there is no wildcard caseadd case _: as an explicit default that raises or logs
timeit results vary wildlyother processes or thermal throttling interfereduse best-of-N (the default) and close heavy apps before benchmarking

Key Results

  • Refactored nested loops into comprehensions with measurable speedup.
  • Implemented a match dispatcher covering 100% of cases plus default.
  • Flattened logic using early returns to cut nesting depth.
  • Benchmarked patterns with timeit rather than guessing.

Key Takeaways

  • Comprehensions replace simple filter/transform loops with faster, more readable code.
  • match is a dispatch table: patterns plus a wildcard default beat long if/elif chains.
  • Benchmark with timeit before claiming one style is faster.
  • Keep comprehensions shallow; extract complex logic into named functions.
  • Scope rules differ: comprehension loop variables do not leak in Python 3.

Practice Challenges

Challenge 1: Dispatch table

Goal: Rewrite the route() function to dispatch on (command, subcommand) tuple patterns with guards, handling at least four combinations.

Hint: Tuple patterns like case ("user", "add") destructure directly; add if clauses as guards.

Challenge 2: Comprehension golf

Goal: Rewrite a triple-nested loop that builds a filtered list into a single comprehension, then benchmark both versions with timeit.

Hint: Keep the comprehension under two conditions; extract anything hairier into a helper.