Structural Pattern Matching
Structural pattern matching is Python's match/case statement (3.10+) that branches on the shape of data — the structure of a list, dict, or object — while pulling out its pieces in the same step.
Learn Structural Pattern Matching in our free Python course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Think of it as a far smarter if/elif chain: it can destructure sequences, mappings, and class instances, capture values, use a wildcard, and add guard conditions.
A match tests a value against each case top to bottom. A bare name captures the value; _ is the catch-all default:
Patterns can match the structure of a list or tuple and bind its parts — including a *rest to soak up the remainder:
Sequence patterns match lists and tuples by length and shape. The *rest star works just like unpacking — it captures however many items are left over.
Match dictionaries by their keys, class instances by their fields, and refine any case with an if guard:
Replace each ___ to complete the patterns so the output matches.
❌ Expecting a bare name to compare, not capture
✅ A bare name always captures. To compare against a constant, use a dotted name ( case Status.PENDING: ) or a literal ( case "pending": ).
✅ Cases run top to bottom. Put case _: (and other broad patterns) last .
✅ Structural pattern matching arrived in Python 3.10. On older versions, fall back to if/elif and isinstance .
Build a tiny interpreter that matches command lists of different shapes.
Go deeper with the official Python documentation:
Lesson complete — you match on shape now!
You can branch with match/case using literal, capture, and wildcard patterns, destructure sequences and mappings, match class instances, and refine cases with if guards.
🚀 Up next: Unpacking & Star Expressions — the destructuring tricks that power so many of these patterns.
Practice quiz
In match/case, what does a bare name pattern like 'case code:' do?
- Compares the subject to a variable named code
- Raises an error because names are not allowed
- Always matches and captures the subject into code
- Only matches if code was defined earlier
Answer: Always matches and captures the subject into code. A bare name is a capture pattern: it always matches and binds the subject to that name.
Which Python version first introduced structural pattern matching (match/case)?
- Python 3.10
- Python 3.8
- Python 3.9
- Python 3.12
Answer: Python 3.10. match/case was added in Python 3.10. Older versions raise a SyntaxError.
Given case 500 | 502 | 503:, what kind of pattern is this?
- A guard pattern
- A bitwise-or computation
- A sequence pattern
- An OR pattern matching any of the listed values
Answer: An OR pattern matching any of the listed values. The | combines alternative patterns, so the case matches 500, 502, or 503.
What does 'case [first, *rest]:' bind when matching [1, 2, 3, 4]?
The *rest star captures the remaining items, so first=1 and rest=[2, 3, 4].
What is the purpose of 'case _:' in a match statement?
- It is a wildcard catch-all that matches anything not caught earlier
- It matches only None
- It captures the value into a variable named underscore
- It marks the end of the match block
Answer: It is a wildcard catch-all that matches anything not caught earlier. The wildcard _ matches anything and does not bind a name, acting like a default branch.
Where should the catch-all 'case _:' be placed?
- First, so it runs before other cases
- Anywhere, order does not matter
- Last, because cases are tested top to bottom
- It must be the only case
Answer: Last, because cases are tested top to bottom. Cases run top to bottom; a catch-all placed first would prevent later cases from ever running.
Which case matches the dict {"type": "click", "x": 10, "y": 20}?
- case {"type": "key", "code": code}:
- case {"type": "click", "x": x, "y": y}:
Answer: case {"type": "click", "x": x, "y": y}:. A mapping pattern matches by keys and binds the corresponding values, so x=10 and y=20.
What does an if-guard add to a case, e.g. case {"delta": d} if d > 0:?
- It loops the case until d > 0
- It ignores the pattern and just checks the condition
- It makes the case optional
- It only matches when both the pattern matches AND the condition is true
Answer: It only matches when both the pattern matches AND the condition is true. A guard is an extra condition; the case fires only when the pattern matches and the guard is true.
For class patterns, what does 'case Point(x=0, y=0):' require?
- Any Point instance regardless of fields
- A Point whose x and y attributes both equal 0
- A dict with keys x and y
- A list of two zeros
Answer: A Point whose x and y attributes both equal 0. A class pattern matches an instance of Point and checks the given fields against the literals.
To compare a subject against a constant instead of capturing it, you should use:
- A bare lowercase name like case pending:
- A wildcard case _:
- A literal or a dotted name, e.g. case "pending": or case Status.PENDING:
- A *star pattern
Answer: A literal or a dotted name, e.g. case "pending": or case Status.PENDING:. Bare names capture; use a literal or a dotted (qualified) name to compare against a constant.