Structural Pattern Matching (PEP 634)

Master Python structural pattern matching: AST grammar, pattern destructuring engines, guard conditions, __match_args__ class patterns, and MATCH bytecode instructions.

02. Control Flow, Pattern Matching, Iteration Protocol & Coroutines7 min read

Introduced in Python 3.10 via PEP 634, Structural Pattern Matching provides declarative destructuring and pattern resolution over arbitrary data shapes.

Unlike traditional switch statements in C or Java that evaluate simple scalar equality, Python's match/case construct inspects object structure, validates sequence lengths, confirms mapping keys, extracts class attributes, and binds matched components to local variables in a single expressive statement.

Pattern Matching StandardLanguage Spec
PEP 634

Introduced dedicated pattern matching bytecodes in Python 3.10 for high-performance structural destructuring.

1. Formal Mathematical Pattern Semantics#

Let v∈Ov \in \mathcal{O} represent the target evaluation object, and PP represent a composite structural pattern. Pattern evaluation is defined as a boolean match predicate M(v,P)∈{True,False}\mathcal{M}(v, P) \in \{\text{True}, \text{False}\} combined with a substitution environment σ:Var→O\sigma: \text{Var} \to \mathcal{O}:

\text{True} \land \sigma \cup \{x \mapsto v\} & \text{if } P = \text{Capture}(x) \\ v == c & \text{if } P = \text{Literal}(c) \\ \bigwedge_{i=0}^{k-1} \mathcal{M}(v[i], P_i) \land (\text{len}(v) == k) & \text{if } P = \text{Sequence}(P_0, \dots, P_{k-1}) \\ \bigwedge_{k_i \in K} \mathcal{M}(v[k_i], P_{k_i}) \land (K \subseteq \text{Keys}(v)) & \text{if } P = \text{Mapping}(\{k_i \mapsto P_{k_i}\}) \\ \text{isinstance}(v, T) \land \bigwedge_{j} \mathcal{M}(\text{getattr}(v, a_j), P_j) & \text{if } P = \text{Class}(T, a_1 \mapsto P_1, \dots)

If an optional guard expression if G is attached to a case branch, the overall match succeeds if and only if M(v,P)=True\mathcal{M}(v, P) = \text{True} and G(σ)=TrueG(\sigma) = \text{True}.

Architecture Diagram
Rendering diagram...

2. Pattern Matching Syntax & Types#

Inspect the diverse pattern forms supported by the Python pattern matching engine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from dataclasses import dataclass @dataclass class Command: action: str target: str def process_event(event: object) -> str: match event: # Literal pattern case "QUIT": return "Terminating application" # Sequence pattern with wildcard capture case ["MOVE", x, y] if isinstance(x, (int, float)) and isinstance(y, (int, float)): return f"Moving to coordinate ({x}, {y})" # Mapping pattern (sub-dict matching) case {"type": "NOTIFICATION", "payload": message}: return f"Received notification: {message}" # Class pattern with attribute extraction case Command(action="DELETE", target=file_path): return f"Deleting file at {file_path}" # Catch-all wildcard case _: return "Unrecognized event format"

3. Class Patterns & The __match_args__ Protocol#

When matching against custom class instances, how does Python know which positional arguments map to which instance attributes?

Python relies on the __match_args__ class attribute: a tuple of attribute name strings. Dataclasses and namedtuples generate __match_args__ automatically:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point2D: # Explicitly define positional attribute mapping order __match_args__ = ("x", "y") def __init__(self, x: float, y: float): self.x = x self.y = y def inspect_point(p: object): match p: # Matches if isinstance(p, Point2D) and binds p.x -> a, p.y -> b case Point2D(0, 0): print("At the origin") case Point2D(a, 0): print(f"On X axis at x={a}") case Point2D(0, b): print(f"On Y axis at y={b}") case Point2D(a, b): print(f"At coordinate ({a}, {b})")

4. Dedicated Pattern Matching Bytecodes#

Under the hood, CPython uses dedicated bytecode opcodes to evaluate patterns with maximum efficiency:

OpcodeOperational Behavior
MATCH_SEQUENCEChecks whether the target object implements the sequence protocol and is not an atomic string or byte buffer.
MATCH_MAPPINGChecks whether the target object implements the mapping protocol (PyDict_Check or PyMapping_Check).
MATCH_KEYSChecks whether keys specified in a mapping pattern exist in the target dictionary and pushes matching values.
MATCH_CLASSValidates isinstance() against the target class and extracts positional attributes mapped via __match_args__.
Strings are Not Sequences in Pattern Matching
In standard Python, str and bytes implement the sequence protocol. However, inside match/case blocks, MATCH_SEQUENCE explicitly excludes strings and byte buffers! This prevents strings from inadvertently matching sequence patterns like [first, *rest].

5. Concept Verification#

Test your understanding of structural pattern matching:

Concept check

How does Python determine which class attributes correspond to positional parameters in class patterns (e.g. case Point(x, y):)?

Concept check

Why does the MATCH_SEQUENCE opcode explicitly exclude str and bytes instances from matching sequence patterns?