Python Match/Case: Structural Pattern Matching Guide
Jul 08, 2026 5 Min Read 56 Views
(Last Updated)
Before Python 3.10, complex branching often required nested if-elif-else statements with multiple isinstance() checks and dictionary lookups. Python 3.10 introduced structural pattern matching (match/case) through PEP 634, allowing code to python match and unpack data structures, dictionaries, and objects in a clean, readable way. It makes complex decision-making logic easier to write and maintain.
Table of contents
- TL;DR Summary
- Basic Syntax and How match/case Works
- Pattern Types with Examples
- Sequence Patterns Matching Lists and Tuples
- Mapping Patterns Matching Dictionaries
- Class Patterns Matching Object Instances
- OR Patterns and Guards
- Python Pattern Types: Quick Reference
- Real-World Use Cases
- Common Mistakes with match/case
- Conclusion
- FAQs
- 1. What is structural pattern matching in Python?
- Is Python match/case the same as a switch statement?
- What Python version is required for match/case?
- What is a wildcard pattern in Python match?
- What is the difference between a capture pattern and a literal pattern?
- How do guard conditions work in Python match?
- Can I use match/case with custom classes?
TL;DR Summary
- Python 3.10 introduced structural pattern matching (match/case) through PEP 634, enabling developers to match and unpack sequences, dictionaries, and objects in a single, readable statement.
- More powerful than a traditional switch statement, it reduces complex if-elif chains and isinstance() checks, making code cleaner and easier to maintain.
Want to master modern Python from structural pattern matching and type hints through data science, automation, and backend development with structured projects and interview preparation? Check out HCL GUVI’s Python Programming Course designed for developers who want to go from Python basics to production-ready code.
Basic Syntax and How match/case Works
The structure of a match statement is straightforward. The subject, the value being inspected, is written after the match keyword. Each case block defines a pattern; Python tests them top to bottom and runs the first one that matches. The underscore wildcard (_) acts as a catch-all default.
def http_status(status):
match status:
case 200:
return 'OK'
case 400:
return 'Bad Request'
case 404:
return 'Not Found'
case 500:
return 'Internal Server Error'
case _:
return 'Unknown status'
print(http_status(404)) # Not Found
print(http_status(301)) # Unknown status
Unlike a C-style switch, there is no fall-through between cases. The first matching case executes and the match block exits. The wildcard _ is not a variable it matches anything without binding the value.
Read More: Python List Comprehensions and Generator Expressions Explained
Want to master modern Python from structural pattern matching and type hints through data science, automation, and backend development with structured projects and interview preparation? Check out HCL GUVI’s Python Programming Course designed for developers who want to go from Python basics to production-ready code.
Pattern Types with Examples
Sequence Patterns Matching Lists and Tuples
def describe_point(point):
match point:
case [0, 0]:
return 'Origin'
case [x, 0]:
return f'On x-axis at {x}'
case [0, y]:
return f'On y-axis at {y}'
case [x, y]:
return f'Point at ({x}, {y})'
case _:
return 'Not a 2D point'
print(describe_point([3, 0])) # On x-axis at 3
print(describe_point([2, 5])) # Point at (2, 5)
Mapping Patterns Matching Dictionaries
def handle_event(event):
match event:
case {'type': 'click', 'button': btn}:
return f'Mouse click: button {btn}'
case {'type': 'keypress', 'key': k}:
return f'Key pressed: {k}'
case {'type': t}:
return f'Unknown event type: {t}'
print(handle_event({'type': 'click', 'button': 'left'}))
# Mouse click: button left
Mapping patterns do partial matching a dictionary with extra keys still matches as long as all the keys listed in the pattern are present. This makes them ideal for handling JSON payloads and API responses where additional fields may exist.
Class Patterns Matching Object Instances
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
@dataclass
class Circle:
centre: Point
radius: float
def describe_shape(shape):
match shape:
case Circle(centre=Point(x=0, y=0), radius=r):
return f'Circle at origin, radius {r}'
case Circle(centre=Point(x=cx, y=cy), radius=r):
return f'Circle at ({cx},{cy}), radius {r}'
case Point(x=0, y=0):
return 'Origin point'
print(describe_shape(Circle(Point(0, 0), 5)))
# Circle at origin, radius 5
OR Patterns and Guards
def classify(value):
match value:
case 'yes' | 'y' | 'true':
return 'Affirmative'
case 'no' | 'n' | 'false':
return 'Negative'
case int(n) if n > 0: # guard condition
return f'Positive integer: {n}'
case int(n) if n < 0:
return f'Negative integer: {n}'
case _:
return 'Unrecognised'
print(classify('yes')) # Affirmative
print(classify(42)) # Positive integer: 42
Python’s structural pattern matching, introduced in Python 3.10, was inspired by pattern matching features found in languages such as Scala, Haskell, Erlang, and Rust. The feature is specified across three companion Python Enhancement Proposals: PEP 634 (Specification), PEP 635 (Motivation and Rationale), and PEP 636 (Tutorial). Together, they provide one of the most comprehensive documentations ever published for a major Python language feature.
Python Pattern Types: Quick Reference
| Pattern Type | Syntax Example | What It Matches |
| Literal | case 42: | Exact value — integer, string, bool, None |
| Capture | case x: | Any value; binds it to variable x |
| Wildcard | case _: | Any value; discards it (default/else) |
| Sequence | case [x, y]: | A list or tuple with exactly two elements |
| Mapping | case {‘key’: v}: | A dict containing the given key; binds value to v |
| Class | case Point(x=a, y=b): | An instance of Point; binds attributes to a, b |
| OR pattern | case ‘yes’ | ‘y’: | Any of the listed literals |
| Guard | case n if n > 0: | Matches n and only if the guard condition is true |
Real-World Use Cases
Structural pattern matching is not just a cleaner switch it shines in scenarios where the shape and content of data varies unpredictably.
- API and JSON response handling: Match on response dictionaries by key presence to route success, error, and pagination responses without nested isinstance and .get() chains.
- Command-line parsers: Match tokenised command strings like [‘move’, ‘up’, ‘5’] or [‘quit’] to dispatch actions cleanly without lengthy elif blocks.
- State machines: Match a (state, event) tuple to transition logic — the combination of sequence patterns and guards makes state transition tables readable and compact.
- AST and compiler tools: Match node types in abstract syntax trees when writing linters, transpilers, or code analysis tools that inspect Python or custom language ASTs.
Common Mistakes with match/case
1. Treating capture patterns like equality checks: case x: does not check whether the subject equals a variable named x it always matches and binds the subject to x. To compare against an existing variable, use a dotted name (case Status. OK:) or a guard (case n if n == threshold:).
2. Expecting fall-through behaviour: Python match has no fall-through. Each case is independent. If you need to share logic between cases, extract it into a function and call it from multiple case blocks do not rely on sequential execution.
3. Using match/case below Python 3.10: The match and case keywords are syntax errors in Python 3.9 and below. Check your interpreter version with python –version before deploying code that uses structural pattern matching, especially in shared or containerised environments.
Conclusion
Structural pattern matching is one of the most expressive additions to Python in years. It replaces brittle chains of isinstance checks and nested conditionals with a declarative syntax that simultaneously tests the shape of data, extracts its components, and routes execution all in one readable block. Literal patterns handle simple value dispatch. Sequence patterns destructure lists and tuples. Mapping patterns extract dictionary values without .get() boilerplate. Class patterns match object instances by attribute. OR patterns and guards add precision without nesting.
FAQs
1. What is structural pattern matching in Python?
Structural pattern matching, introduced in Python 3.10 via PEP 634, is a control-flow feature that lets you match a value against a series of patterns using match and case keywords. Unlike a simple switch statement,
2. Is Python match/case the same as a switch statement?
No. A switch statement in languages like Java or C tests a single value for equality against a list of constants. Python’s match tests a subject against structural patterns it can inspect the shape of a list, the keys present in a dictionary, the type and attributes of an object, and apply conditional guards. It is far more powerful than a traditional switch.
3. What Python version is required for match/case?
Python 3.10 or higher is required. The match and case keywords are valid syntax only from 3.10 onwards they are syntax errors in 3.9 and earlier. You can check your version with python –version in the terminal. Python 3.10 was released in October 2021.
4.What is a wildcard pattern in Python match?
The wildcard pattern is written as a single underscore (_). It matches any value without binding it to a variable. Placed as the last case in a match block, it acts as the default catch-all equivalent to the else clause in an if-elif-else chain. Unlike a capture pattern (case x:), the value is discarded.
5. What is the difference between a capture pattern and a literal pattern?
A literal pattern (case 42: or case ‘error’:) tests the subject for equality against a specific value. A capture pattern (case x:) always matches and binds the subject value to the variable name it never tests equality. This is a common source of bugs: case existing_var: does not compare against existing_var; it shadows it with a new binding.
6. How do guard conditions work in Python match?
A guard is an if clause appended to a case pattern: case n if n > 0:. The pattern must match first; then the guard condition is evaluated. If the guard is False, Python moves to the next case even though the pattern itself matched. Guards are essential for adding numeric range checks or attribute comparisons that patterns alone cannot express.
7. Can I use match/case with custom classes?
Yes. Class patterns match instances of a specific class and extract named attributes. For dataclasses and named tuples, this works automatically. For plain classes, you define a __match_args__ class variable a tuple of attribute names to enable positional pattern matching. Named attribute matching (case Point(x=a, y=b):) works without __match_args__.



Did you enjoy this article?