Pattern Matching
Beyond switch/case
Section titled “Beyond switch/case”TypeScript’s switch matches on a single value. Python’s match/case (introduced in Python 3.10) is structural — it can destructure sequences, dictionaries, and class instances simultaneously. Think of it as TypeScript’s switch + array/object destructuring fused into one statement.
Sequence patterns
Section titled “Sequence patterns”// TypeScript — switch + destructuringconst command = ["move", 10, 20];
if (Array.isArray(command) && command[0] === "move") { const [, x, y] = command; console.log(`Move to (${x}, ${y})`);} else if (command[0] === "stop") { console.log("Stopping");} else { console.log("Unknown command");}command = ["move", 10, 20]
match command: case ["move", x, y]: print(f"Move to ({x}, {y})") case ["stop"]: print("Stopping") case _: print("Unknown command")
# Output: Move to (10, 20)The case ["move", x, y] pattern checks the list length, checks that the first element equals "move", and binds x and y to the second and third elements — all in one step.
Mapping patterns
Section titled “Mapping patterns”Mapping patterns match dictionary shapes, similar to TypeScript object destructuring in a switch.
// TypeScript — discriminated union patterntype Event = | { type: "click"; x: number; y: number } | { type: "keypress"; key: string };
function handle(event: Event) { switch (event.type) { case "click": console.log(`Click at (${event.x}, ${event.y})`); break; case "keypress": console.log(`Key: ${event.key}`); break; }}event = {"type": "click", "x": 100, "y": 200}
match event: case {"type": "click", "x": x, "y": y}: print(f"Click at ({x}, {y})") case {"type": "keypress", "key": key}: print(f"Key pressed: {key}") case _: print("Unknown event")
# Output: Click at (100, 200)Class patterns
Section titled “Class patterns”Class patterns destructure dataclass (or any class with __match_args__) instances.
// TypeScript — instanceof + destructuringclass Point { constructor(public x: number, public y: number) {} }
function describe(p: Point): string { if (p.x === 0 && p.y === 0) return "Origin"; if (p.x === 0) return `Y-axis at ${p.y}`; if (p.y === 0) return `X-axis at ${p.x}`; return `At (${p.x}, ${p.y})`;}from dataclasses import dataclass
@dataclassclass Point: x: int y: int
point = Point(0, 5)
match point: case Point(x=0, y=0): print("Origin") case Point(x=0, y=y): print(f"On Y-axis at {y}") case Point(x=x, y=0): print(f"On X-axis at {x}") case Point(x=x, y=y): print(f"At ({x}, {y})")
# Output: On Y-axis at 5Guard conditions (if clauses)
Section titled “Guard conditions (if clauses)”You can add an if guard to any case arm — the equivalent of TypeScript’s conditional switch trick.
// TypeScript — no clean guard syntax in switchfunction grade(score: number): string { if (score >= 90) return "A"; if (score >= 80) return "B"; if (score >= 70) return "C"; return "F";}def grade(score: int) -> str: match score: case s if s >= 90: return "A" case s if s >= 80: return "B" case s if s >= 70: return "C" case _: return "F"
print(grade(85)) # BTry it
Section titled “Try it”from dataclasses import dataclass
@dataclassclass Point: x: int y: int
# Sequence patternfor cmd in [["move", 5, 10], ["stop"], ["jump", 3], ["move", 0, 0]]: match cmd: case ["move", x, y]: print(f" move -> ({x}, {y})") case ["stop"]: print(" stop") case _: print(f" unknown: {cmd}")
print()
# Mapping patternevents = [ {"type": "click", "x": 10, "y": 20}, {"type": "keypress", "key": "Enter"}, {"type": "resize", "w": 800, "h": 600},]for evt in events: match evt: case {"type": "click", "x": x, "y": y}: print(f" click ({x},{y})") case {"type": "keypress", "key": k}: print(f" key: {k}") case {"type": t}: print(f" other event: {t}")
print()
# Class pattern with guardsfor pt in [Point(0, 0), Point(0, 7), Point(3, 0), Point(3, 4)]: match pt: case Point(x=0, y=0): print(" origin") case Point(x=0, y=y) if y > 0: print(f" +Y axis at {y}") case Point(x=x, y=0) if x > 0: print(f" +X axis at {x}") case Point(x=x, y=y): print(f" quadrant ({x},{y})")Loading Python runtime (first run only)…