Pattern Matching
Pattern Matching — The Complete Guide
Understand structural pattern matching with Python's match/case statement — a powerful, readable alternative to long if-elif chains.
Introduction
Pattern matching is a technique that checks a value against a structure or shape and, when it matches,
extracts data from it. Python 3.10 introduced structural pattern matching via the match/case
statement — far more powerful than a simple switch. It can match literals, types, sequences, mappings, and even the internal
structure of objects, binding parts of the data to variables as it goes.
Real-World Analogy
The Mail Sorting Machine
A postal sorter looks at each envelope's shape and label: letters go one way, parcels another, priority mail a third. It matches
each item against known patterns and routes it accordingly. match/case is that sorter for your data — it
inspects the shape and sends execution down the right path.
Prerequisites
Before You Start
- Python 3.10 or newer (structural pattern matching is not available earlier)
- Understanding of
if/elif/elseconditionals - Familiarity with lists, tuples, dictionaries, and classes
- Basic knowledge of tuple unpacking (e.g.,
a, b = point) - A Python 3.10+ interpreter to run the examples
Basic Syntax
The match statement compares a subject value against several case patterns, running the first that matches.
def http_status(code):
match code:
case 200:
return "OK"
case 404:
return "Not Found"
case 500:
return "Server Error"
case _: # wildcard — matches anything
return "Unknown"
print(http_status(404)) # Not Found
_ is the wildcard pattern — it matches anything and acts like the default case.
Core Pattern Types
Literal & Capture Patterns
Match exact values or bind to a name.
A literal like 200 matches that value; a bare name like x captures the subject into the variable x.
Sequence Patterns
Match lists/tuples by shape.
[x, y] matches a two-element sequence and binds its items; [first, *rest] captures the remainder.
Mapping & Class Patterns
Match dicts and object structure.
{"key": value} matches dictionaries; Point(x=0, y=0) matches objects and extracts attributes.
Matching Sequences
Pattern matching shines when destructuring lists and tuples.
def describe(point):
match point:
case [0, 0]:
return "Origin"
case [0, y]:
return f"On Y-axis at {y}"
case [x, 0]:
return f"On X-axis at {x}"
case [x, y]:
return f"Point at ({x}, {y})"
case _:
return "Not a 2D point"
print(describe([0, 5])) # On Y-axis at 5
print(describe([3, 4])) # Point at (3, 4)
Capturing the rest with *
match [1, 2, 3, 4]:
case [first, *rest]:
print(first) # 1
print(rest) # [2, 3, 4]
Matching Dictionaries
def handle(event):
match event:
case {"type": "click", "x": x, "y": y}:
return f"Click at ({x}, {y})"
case {"type": "key", "value": v}:
return f"Key pressed: {v}"
case {"type": t}:
return f"Unhandled event: {t}"
print(handle({"type": "click", "x": 10, "y": 20})) # Click at (10, 20)
Matching Class Instances
You can match against object structure and pull out attributes directly.
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def locate(p):
match p:
case Point(x=0, y=0):
return "Origin"
case Point(x=0, y=y):
return f"On Y-axis at {y}"
case Point(x=x, y=0):
return f"On X-axis at {x}"
case Point(x=x, y=y):
return f"At ({x}, {y})"
print(locate(Point(0, 7))) # On Y-axis at 7
Guards (Extra Conditions)
Add an if guard to a case to match only when an extra condition is true.
def classify(point):
match point:
case [x, y] if x == y:
return "On the diagonal"
case [x, y] if x > 0 and y > 0:
return "First quadrant"
case [x, y]:
return "Somewhere else"
print(classify([4, 4])) # On the diagonal
print(classify([2, 5])) # First quadrant
OR Patterns and Binding
def category(command):
match command:
case "start" | "run" | "go": # OR pattern
return "Begin execution"
case "stop" | "halt" | "end":
return "Stop execution"
case str() as text: # capture with type check
return f"Unknown command: {text}"
print(category("go")) # Begin execution
print(category("pause")) # Unknown command: pause
match/case vs if/elif
| Aspect | match/case | if/elif |
|---|---|---|
| Best for | Matching structure/shape of data | Arbitrary boolean conditions |
| Destructuring | Built-in (binds variables) | Manual unpacking needed |
| Readability | Clean for many shapes | Gets verbose with many branches |
| Python version | 3.10+ | All versions |
Readability Insight
Handling \(N\) distinct data shapes with nested if/elif often needs manual type checks and unpacking, growing the
cognitive load roughly with the number of conditions per branch. With pattern matching, each shape maps to a single declarative case:
\[ \text{Branches} = N \qquad\Rightarrow\qquad \text{Cases} = N \;\text{(one clean case each)} \]
The win isn't algorithmic speed — it's clarity: one shape, one case, with automatic binding.
Best Practices
Do This
- Use
matchfor matching data shape; keepiffor simple boolean logic - Always include a wildcard
case _to handle unexpected values - Use guards (
case ... if ...) for extra conditions instead of nesting - Prefer class patterns with dataclasses for clean object matching
- Remember bare names capture — use literals or dotted names for constants
- Order cases from most specific to most general
Common Mistakes
case CONSTANT: to compare with a variable — a bare name captures the value instead of comparing to it.
case Color.RED: or a literal to compare against a known value.
case _ — unmatched values silently fall through and do nothing.
Interview Questions
| Question | Short Answer |
|---|---|
| What is structural pattern matching? | Matching a value against a structure/shape and binding parts of it, via match/case. |
| Which Python version introduced it? | Python 3.10. |
What does case _ do? |
It's the wildcard — matches anything, acting as the default case. |
| What is a guard? | An if condition on a case that must also be true for it to match. |
| How is it different from a switch? | It matches structure and destructures data, not just equality on a value. |
Quick Revision
| Pattern | Example | Matches |
|---|---|---|
| Literal | case 200: |
Exact value 200 |
| Capture | case x: |
Anything, binds to x |
| Sequence | case [a, b]: |
Two-element list/tuple |
| Mapping | case {"k": v}: |
Dict with key "k" |
| Class | case Point(x=0): |
Point with x == 0 |
| OR | case "a" | "b": |
Either value |
| Wildcard | case _: |
Anything (default) |
Key Takeaways
Structural pattern matching (match/case, Python 3.10+) matches data by its shape —
literals, sequences, mappings, and objects — while binding the pieces you need. Use guards for extra
conditions, always add a wildcard, and reach for it when if/elif chains get unwieldy.