Back to Blog
Python

Python Match Statement: Structural Pattern Matching

python match statement: Learn how Python's match statement implements structural pattern matching, including literal, capture, sequence, mapping, and class patterns wi...

pythonstructural pattern matchingpython 3.10control flowmatch case
Illustration of Python's match statement routing data through multiple pattern branches

Python 3.10 introduced the match statement, which implements structural pattern matching. The python match statement is not a switch statement: it does not compare a value against a list of constants. Instead, it evaluates a subject expression once and attempts to bind it against a series of patterns, executing the first case whose pattern matches.

How the Match Statement Is Structured

The basic form is:

match subject: case pattern1: # body case pattern2: # body

The subject expression is evaluated exactly once, then compared against each pattern in order. When a pattern matches, its case body runs and the match block ends. There is no fall-through, so you do not need an explicit break after each case.

Python supports several pattern types, each with a distinct behavior:

Pattern typeSyntax exampleBehavior
Literalcase 42:Matches an exact value
Capturecase x:Binds the matched value to a name
Wildcardcase _:Matches anything without binding
Sequencecase [a, b]:Destructures a sequence
Mappingcase {"key": v}:Checks for keys and binds values
Classcase Point(x=0):Matches object attributes
ORcase 1 | 2:Matches any listed alternative

The wildcard _ is typically placed last as a fallback:

def classify(value): match value: case 0: return "zero" case 1: return "one" case _: return "other"

Literal and Value Patterns

Literal patterns match against exact values. Integers, strings, booleans, and None can appear directly in a pattern. This is the closest the match statement comes to a conventional switch.

Value patterns, written with a dotted name such as Color.RED, compare using equality against the referenced value. A bare name like RED is not a value pattern; it is a capture pattern, which behaves differently.

Capturing Patterns and the Wildcard

A bare name in a pattern captures the matched value into a new variable:

def parse_response(response): match response: case {"status": 200, "body": body}: return body case {"status": status}: return f"error status {status}"

The capture pattern body binds the value associated with the "body" key. The variable is available only inside that case block.

The wildcard _ does not bind anything. You cannot reference the value it matched, which is intentional: it exists purely to match anything without capturing.

Sequence Patterns for Structured Data

Sequence patterns match against lists, tuples, and other sequence types. They allow you to destructure the subject directly:

def process_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"at ({x}, {y})"

A sequence pattern can use *rest to capture the remaining elements:

def split_command(args): match args: case [command, *flags]: return command, flags

The pattern [command, *flags] requires at least one element. The first element binds to command, and the rest binds to flags as a list.

Mapping Patterns for Dictionary Input

Mapping patterns match against dictionaries and check for the presence of specific keys:

def handle_request(payload): match payload: case {"type": "ping"}: return "pong" case {"type": "send", "message": msg}: return f"sending: {msg}" case _: return "unsupported payload"

A mapping pattern does not require the subject to contain only the keys listed. Extra keys are ignored. If you need to capture the remaining keys, use **rest:

case {"type": "send", **rest}: return rest

Guard Clauses

A guard is an if condition attached to a pattern. The pattern must match first, then the guard is evaluated. If the guard evaluates to false, the match continues to the next case:

def categorize(number): match number: case n if n > 0: return "positive" case n if n < 0: return "negative" case _: return "zero"

Guards are evaluated only after a pattern matches, so they can safely reference variables bound by the pattern.

Class Patterns and OR Patterns

Class patterns let you match against objects and bind attributes:

class Point: def __init__(self, x, y): self.x = x self.y = y def describe(point): match point: case Point(x=0, y=0): return "origin" case Point(x=x, y=y): return f"({x}, {y})"

OR patterns, written with |, let several patterns share one case block:

match value: case 1 | 2 | 3: return "small" case _: return "large"

All alternatives in an OR pattern must bind the same variables.

Performance and Maintainability Considerations

The match statement evaluates the subject once and tests patterns in order. For a small number of patterns, the cost is comparable to an equivalent chain of if statements. The real benefit is readability: complex destructuring logic that would otherwise require nested conditionals and manual unpacking can be expressed declaratively.

One maintainability concern is pattern order. Since patterns are tested top to bottom, more specific patterns must appear before more general ones. A wildcard placed early will capture everything and make later cases unreachable. Similarly, a mapping pattern with fewer keys is more general than one with more keys, so order matters when patterns overlap.

Another consideration is that capture patterns silently shadow existing variables. If a name in a pattern collides with a variable in the enclosing scope, the pattern binds a new variable for the duration of the case block rather than comparing against the outer value. Use value patterns with dotted names when you intend to compare against a constant.

Common Edge Cases

A few behaviors consistently surprise developers new to the match statement. First, the subject is evaluated exactly once, even if multiple patterns are tested. Second, sequence patterns match any sequence type, not just lists and tuples, so a string can match a sequence pattern. Third, mapping patterns ignore extra keys, which is usually what you want but can hide malformed input if you do not also validate the full key set.

The match statement is available only in Python 3.10 and later. If your project must support older versions, you cannot use this syntax at all; you would need an if/elif chain or a third-party library that emulates pattern matching, which will not provide the same syntax.

python match statement: Practical Usage and Code Examples | RYUSLOG DEV