Match Statements
The match statement, added in Python 3.10, compares a value against a series of patterns. It can do what a switch does in other languages, but it goes much further: it can check the shape of lists, tuples, dictionaries and objects, and pull values out of them at the same time.
Matching literal values
def http_status(code): match code: case 200: return "OK" case 301 | 302: return "Redirect" case 404: return "Not Found" case 500: return "Server Error" case _: return "Unknown" for c in [200, 302, 404, 418]: print(c, http_status(c))
200 OK 302 Redirect 404 Not Found 418 Unknown
Cases are tried in order and the first match wins — there's no fall-through, so no break is needed. | combines alternatives, and _ is the wildcard that matches anything.
Capturing values
A bare name in a pattern doesn't compare — it captures whatever is in that position:
def describe(point): match point: case (0, 0): return "origin" case (0, y): return f"on the y-axis at {y}" case (x, 0): return f"on the x-axis at {x}" case (x, y): return f"at ({x}, {y})" for p in [(0, 0), (0, 5), (3, 0), (2, 7)]: print(describe(p))
origin on the y-axis at 5 on the x-axis at 3 at (2, 7)
Sequence patterns
Patterns can check length and capture the rest with * — perfect for parsing commands:
def run(command): match command.split(): case ["quit" | "exit"]: return "Goodbye" case ["go", direction]: return f"Moving {direction}" case ["take", *items] if items: return f"Taking {', '.join(items)}" case ["take"]: return "Take what?" case _: return f"Unknown command: {command!r}" for c in ["go north", "take lamp key", "take", "exit", "dance"]: print(run(c))
Moving north Taking lamp, key Take what? Goodbye Unknown command: 'dance'
Guards
An if after a pattern adds an extra condition — the case only matches if both the pattern and the guard succeed:
def classify(n): match n: case 0: return "zero" case int() if n < 0: return "negative int" case int() if n % 2 == 0: return "even int" case int(): return "odd int" case float(): return "float" case _: return "not a number" for v in [0, -3, 8, 7, 2.5, "7"]: print(repr(v), "->", classify(v))
0 -> zero -3 -> negative int 8 -> even int 7 -> odd int 2.5 -> float '7' -> not a number
int() in a pattern is a class pattern: it matches any instance of int.
Mapping patterns
Dictionary patterns check for keys — very handy for JSON-like data. Extra keys are ignored:
def handle(event): match event: case {"type": "click", "x": x, "y": y}: return f"click at {x},{y}" case {"type": "key", "key": "Enter" | "Return"}: return "submit" case {"type": "key", "key": key}: return f"pressed {key}" case {"type": str(kind)}: return f"unhandled {kind} event" case _: return "invalid event" events = [ {"type": "click", "x": 10, "y": 20, "button": 1}, {"type": "key", "key": "Enter"}, {"type": "key", "key": "a"}, {"type": "scroll"}, {"oops": True}, ] for e in events: print(handle(e))
click at 10,20 submit pressed a unhandled scroll event invalid event
Class patterns
from dataclasses import dataclass @dataclass class Circle: radius: float @dataclass class Rect: width: float height: float def area(shape): match shape: case Circle(radius=r): return 3.14159 * r ** 2 case Rect(width=w, height=h) if w == h: return w * w case Rect(width=w, height=h): return w * h print(area(Circle(1)), area(Rect(3, 3)), area(Rect(2, 5)))
3.14159 9 10
Dataclasses (lesson 40) work naturally with class patterns.
A common gotcha: constants
Because bare names capture, you can't match against a variable directly:
QUIT = "q" def check(key): match key: case "q": return "quit (literal works)" case _: return "other" class Keys: QUIT = "q" def check_dotted(key): match key: case Keys.QUIT: return "quit (dotted name works)" case _: return "other" print(check("q"), "|", check_dotted("q"))
quit (literal works) | quit (dotted name works)
Writing case QUIT: would capture any value into a new variable named QUIT. Use literals, or dotted names like Keys.QUIT or an enum member (Color.RED), which are compared rather than captured.
When to use match
Use match when you're branching on the structure of data — command parsing, message handling, tree processing. For a handful of simple comparisons, if/elif or a dictionary lookup is often simpler.