match/case compilation to MATCH_* opcodes, pattern semantics, and guard evaluation in the interpreter.
Pattern matching is Python’s structural matching system. It is implemented by the match statement and case clauses.
match value:
case 0:
result = "zero"
case [x, y]:
result = x + y
case {"name": name}:
result = name
case _:
result = NonePattern matching is not a switch statement over constants only. It can inspect structure, bind names, test classes, match sequences, match mappings, apply guards, and select a case based on runtime shape.
At the CPython level, pattern matching is compiled into ordinary bytecode plus specialized matching instructions. The interpreter evaluates the subject, tries each case in order, binds names for a successful match, and jumps to the selected body.
37.1 The match Statement
A match statement has one subject expression and one or more cases.
match subject:
case pattern:
body
case pattern if guard:
body
case _:
bodyThe subject expression is evaluated once.
match compute():
case 1:
...
case 2:
...compute() runs one time. The result is then tested against the cases from top to bottom.
Conceptually:
subject = compute()
try case 1
if match succeeds:
execute case 1 body
else:
try case 2Only the first matching case body runs.
37.2 Patterns vs Expressions
Patterns look like expressions, but they follow different rules.
match value:
case x:
...This does not compare value with an existing variable x. It captures the subject into a new name x.
A capture pattern always succeeds.
case x:means:
bind x = subject
match succeedsTo compare against a named constant, use a dotted name:
case Color.RED:or use a guard:
case x if x == expected:This distinction matters because pattern matching has its own syntax and binding rules.
37.3 Case Order
Cases are tried in order.
match value:
case int():
result = "integer"
case bool():
result = "boolean"This is a bad order because bool is a subclass of int.
isinstance(True, int)is true.
So True matches int() before reaching bool().
Better:
match value:
case bool():
result = "boolean"
case int():
result = "integer"Pattern matching uses runtime type and structure. More specific cases should usually appear before more general cases.
37.4 Wildcard Pattern
The wildcard pattern _ matches anything and binds nothing.
match value:
case 0:
result = "zero"
case _:
result = "other"The _ pattern is commonly used as the final fallback.
It differs from a normal capture name:
case name:This binds name.
case _:This does not bind _ as a new local for the match result.
37.5 Literal Patterns
Literal patterns match values such as numbers, strings, bytes, booleans, and None.
match value:
case 0:
result = "zero"
case "ok":
result = "success"
case None:
result = "missing"For most literals, matching uses equality semantics.
subject == literalFor singleton constants such as None, True, and False, matching uses identity-style singleton semantics.
Literal patterns are useful for tags, small states, and protocol markers.
37.6 Capture Patterns
A capture pattern binds a name.
match value:
case x:
result = xThis always matches.
Capture patterns are useful inside larger patterns:
match point:
case [x, y]:
result = x + yHere, [x, y] is a sequence pattern. The names x and y capture elements.
After a successful match, bound names are available in the case body.
match value:
case [x, y]:
print(x, y)If the match fails, bindings from that failed pattern must not leak in a way user code can rely on.
37.7 Value Patterns
A value pattern compares the subject with a value referenced by a dotted name.
match color:
case Color.RED:
handle_red()
case Color.BLUE:
handle_blue()The dotted name is evaluated and compared with the subject.
This is different from:
case RED:which captures into a name called RED.
For constants, use dotted names, enums, or guards.
class Status:
OK = "ok"
ERROR = "error"
match status:
case Status.OK:
...37.8 OR Patterns
An OR pattern matches if any alternative matches.
match value:
case 0 | 1 | 2:
result = "small"All alternatives must bind the same set of names.
Valid:
match value:
case [x] | (x,):
result = xInvalid in principle:
case [x] | [x, y]:because the first alternative binds x, while the second binds x and y.
The compiler checks name-binding consistency for OR patterns.
37.9 AS Patterns
An AS pattern binds the whole matched value while also matching a subpattern.
match value:
case [x, y] as pair:
print(x, y, pair)If the subject matches [x, y], then:
x = first element
y = second element
pair = whole subjectThis is useful when you need both destructured fields and the original value.
37.10 Guards
A guard is an if condition attached to a case.
match value:
case [x, y] if x < y:
result = "ascending"The pattern is matched first. If it succeeds, the guard is evaluated.
Conceptually:
if subject matches [x, y]:
if x < y:
execute body
else:
try next caseGuards can run arbitrary Python code. They can raise exceptions, call functions, mutate state, or depend on captured names.
If a guard evaluates to false, the case is treated as not selected, and matching continues with the next case.
37.11 Sequence Patterns
A sequence pattern matches sequence-like objects.
match value:
case [x, y]:
result = x + yThis matches sequences of length two.
Examples that may match:
[1, 2]
(1, 2)Strings and bytes are not treated as sequences for this purpose, even though they are sequence-like in other contexts.
Sequence matching needs to check:
is the subject a sequence pattern candidate
does it have the required length
extract elements
match nested subpatterns
bind names37.12 Starred Sequence Patterns
A starred pattern captures a variable-length middle section.
match value:
case [first, *middle, last]:
...For:
value = [1, 2, 3, 4]the bindings are:
first = 1
middle = [2, 3]
last = 4The starred capture receives a list.
The pattern requires at least enough elements for the non-starred positions.
case [first, *middle, last]:requires at least two elements.
37.13 Mapping Patterns
A mapping pattern matches mapping-like objects.
match value:
case {"name": name, "age": age}:
...This checks that the subject has the required keys.
For:
value = {"name": "Ada", "age": 37, "city": "London"}the pattern matches and binds:
name = "Ada"
age = 37Extra keys are allowed unless the pattern uses stricter logic outside the pattern, such as a guard.
Mapping patterns can also capture remaining items:
match value:
case {"name": name, **rest}:
...Then rest receives a dictionary of unmatched keys.
37.14 Class Patterns
Class patterns match objects by type and attributes.
match value:
case Point(x, y):
...This checks whether value is an instance of Point, then extracts fields.
The meaning of positional fields is controlled by Point.__match_args__.
Example:
class Point:
__match_args__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = yThen:
match p:
case Point(x, y):
...is conceptually similar to:
isinstance(p, Point)
x = p.x
y = p.ywith pattern-matching semantics and failure behavior.
37.15 Keyword Class Patterns
Class patterns can name attributes explicitly.
match value:
case Point(x=0, y=y):
...This matches a Point whose x attribute matches 0, and binds the y attribute.
Keyword class patterns do not depend on __match_args__ for those attributes.
Conceptually:
check isinstance(value, Point)
get value.x and match against 0
get value.y and bind yAttribute lookup can execute code, so class patterns can have side effects if properties or custom attribute access are involved.
37.16 __match_args__
__match_args__ maps positional class pattern fields to attribute names.
class Point:
__match_args__ = ("x", "y")This allows:
case Point(a, b):to mean:
case Point(x=a, y=b):For dataclasses and named tuples, Python often provides useful __match_args__ automatically.
If __match_args__ is missing, empty, or incompatible, positional class patterns may fail or raise errors depending on the pattern form.
37.17 Nested Patterns
Patterns can nest.
match value:
case {"point": Point(x, y), "label": label}:
...This checks:
subject is a mapping
subject has key "point"
subject has key "label"
subject["point"] is a Point
extract Point.x and Point.y
bind labelNested patterns are compiled into a sequence of tests and extraction operations.
If any nested part fails, the whole case fails and matching continues with the next case.
37.18 Name Binding
Names bound in a successful case become local variables in the surrounding scope.
def f(value):
match value:
case [x, y]:
return x + y
case _:
return 0The compiler treats x and y as local names in f.
This can affect scope analysis. A name captured by a pattern is a binding occurrence, similar to assignment.
Example:
def f(value):
match value:
case x:
return xHere, x is local to f.
37.19 Failed Match Bindings
A failed pattern should not be used as a source of stable bindings.
Example:
match value:
case [x, 0]:
...
case _:
...If the first case fails after binding part of the structure, CPython must avoid exposing partial bindings as successful results.
The language semantics do not let you rely on names bound during failed matches.
Compiler and interpreter code must preserve this rule.
37.20 Pattern Matching Bytecode
Pattern matching compiles into bytecode.
A simple example:
def f(value):
match value:
case [x, y]:
return x + y
case _:
return 0Conceptual bytecode structure:
load subject
try sequence case:
check sequence
check length 2
unpack into x, y
execute body
return
fallback:
return 0CPython includes pattern-specific opcodes for common checks, such as sequence matching, mapping matching, key extraction, and class matching. The exact opcode names and layout can change between versions.
37.21 Subject Duplication
The subject must often remain available while trying multiple cases.
match value:
case [x]:
...
case {"x": x}:
...
case _:
...If the first case fails, the second case still needs the original subject.
The compiler emits stack operations to duplicate, preserve, or discard the subject as needed.
Conceptually:
subject on stack
try case 1
if fail, restore subject
try case 2
if fail, restore subject
fallbackCorrect stack discipline is important because pattern matching has many failure paths.
37.22 Failure Paths
A case can fail at many points:
wrong type
wrong sequence length
missing mapping key
class check fails
attribute extraction fails with AttributeError
nested subpattern fails
OR alternative fails
guard is falseSome failures are normal non-matches. Others are real exceptions.
Pattern matching must distinguish:
normal pattern failure
real runtime exceptionFor example, a missing mapping key means the mapping pattern does not match.
But a property getter that raises RuntimeError during a class pattern should propagate the exception.
37.23 Guards and Exceptions
A guard is ordinary Python code.
match value:
case x if check(x):
...If check(x) raises, the exception propagates. The interpreter does not treat it as a failed match.
Conceptually:
pattern succeeds
evaluate guard
returns true: select case
returns false: try next case
raises: propagate exceptionThis makes guards powerful but also effectful.
37.24 Mapping Key Lookup
Mapping patterns use key lookup. A pattern such as:
case {"name": name}:checks whether the subject contains the key "name" and extracts its value.
The implementation must avoid accidentally invoking behavior that changes semantics compared with mapping-pattern rules.
The important source-level behavior is:
required keys must exist
their values must match subpatterns
extra keys are allowedIf **rest is present, unmatched key-value pairs are copied into a new dictionary.
37.25 Class Pattern Attribute Lookup
Class patterns can perform attribute lookup.
case Point(x=x, y=y):This may call:
Point-related descriptors
properties
__getattribute__
__getattr__If attribute access raises AttributeError, the class pattern can fail as a non-match for that attribute. Other exceptions propagate.
Example:
class C:
@property
def x(self):
raise RuntimeError("bad")
match C():
case C(x=x):
...This propagates RuntimeError.
37.26 Pattern Matching and Descriptors
Because class patterns use attribute access, descriptors can participate.
class C:
@property
def x(self):
return 10
match C():
case C(x=10):
result = "matched"The property getter runs during matching.
This means pattern matching can trigger user code. It is not always a passive structural inspection.
37.27 Pattern Matching and Dataclasses
Dataclasses work naturally with class patterns.
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def f(p):
match p:
case Point(0, y):
return y
case Point(x, y):
return x + yDataclasses typically provide __match_args__ based on fields, so positional class patterns work.
Conceptually:
Point(0, y)
matches Point instance
checks x == 0
binds y from p.y37.28 Pattern Matching and Enums
Enums are often used with value patterns.
from enum import Enum
class TokenKind(Enum):
NAME = 1
NUMBER = 2
match kind:
case TokenKind.NAME:
...
case TokenKind.NUMBER:
...The dotted names are value patterns, so they compare against existing enum members.
Do not write:
case NAME:unless you intend to capture a new variable.
37.29 Pattern Matching and ASTs
Pattern matching is useful for tree-like structures.
match node:
case BinOp(left, "+", right):
...
case Literal(value):
...A compiler or interpreter can use class patterns to inspect node types.
Example:
@dataclass
class Literal:
value: object
@dataclass
class BinOp:
left: object
op: str
right: objectThen:
match node:
case BinOp(Literal(a), "+", Literal(b)):
return Literal(a + b)This expresses structural decomposition directly.
37.30 Pattern Matching and Bytecode Stack
Pattern matching uses the frame value stack like other bytecode.
The subject, intermediate extracted values, comparison results, and preserved fallback values may live on the stack.
A sequence pattern:
case [x, y]:conceptually does:
subject
check sequence
check length
unpack two elements
store x
store y
execute bodyIf it fails, temporary values are popped and the next case starts from a clean stack shape.
37.31 Compiler Responsibilities
The compiler must generate bytecode that preserves several invariants:
subject evaluated once
cases tried in order
successful pattern bindings visible in body
failed pattern bindings not relied on
guards evaluated after binding
guards can access bound names
stack shape valid at every jump
exceptions propagate correctly
only first successful case runsPattern matching is therefore both a language feature and a compiler feature.
37.32 Runtime Responsibilities
At runtime, CPython must perform:
type checks
sequence checks
mapping checks
class checks
attribute extraction
key extraction
equality checks
binding
guard evaluation
failure jumps
exception propagationSome of these operations are generic Python operations and can call user code.
For example:
case SomeClass(x=x):may execute descriptor logic for x.
case 10:may use equality comparison.
Pattern matching must remain correct under Python’s dynamic object model.
37.33 Pattern Matching and Equality
Literal and value patterns can use equality.
case 10:matches if the subject equals 10.
For user-defined objects, equality can call __eq__.
class C:
def __eq__(self, other):
print("compare")
return True
match C():
case 10:
print("matched")This can execute user code.
If equality raises an exception, the exception propagates.
37.34 Pattern Matching and Performance
Pattern matching can be clear, but it is not magic. It still performs runtime checks.
Costs may include:
type checks
length checks
mapping lookups
attribute lookups
equality comparisons
temporary allocations
guard execution
bytecode branchesA simple if chain may be faster for very small scalar cases.
Pattern matching is most useful when structure matters:
nested data
AST nodes
protocol messages
token streams
configuration shapes
command parsing
event dispatchUse it where it improves clarity.
37.35 Pattern Matching vs if/elif
This:
match value:
case 0:
...
case 1:
...can be similar to:
if value == 0:
...
elif value == 1:
...But pattern matching goes further:
match value:
case {"type": "user", "name": name}:
...
case ["move", x, y]:
...
case Point(x=0, y=y):
...It combines type tests, shape tests, unpacking, and binding.
37.36 Pattern Matching vs Destructuring Assignment
Destructuring assignment requires the shape to match or raises.
x, y = valueIf value does not have exactly two items, it raises.
Pattern matching tests shape and can fall through.
match value:
case [x, y]:
...
case _:
...Failure to match [x, y] simply tries the next case.
So pattern matching is conditional destructuring.
37.37 Pattern Matching and Scope
Names captured in patterns are local bindings.
def f(value):
match value:
case x:
return xThe compiler treats x as local to f.
This can surprise users expecting a comparison to an outer variable.
expected = 10
def f(value):
match value:
case expected:
return TrueThis captures into a local name expected. It does not compare to the outer expected.
Use:
def f(value):
match value:
case x if x == expected:
return Trueor use a dotted constant.
37.38 Pattern Matching and Unreachable Cases
A capture pattern catches everything.
match value:
case x:
...
case 0:
...The second case is unreachable.
Similarly:
case _:as a non-final case makes later cases unreachable.
The compiler detects some irrefutable patterns in invalid positions.
An irrefutable pattern is one that always matches, such as:
_
x
object()in many contexts.
37.39 Pattern Matching and object()
A class pattern with no arguments can match any instance of that class.
case object():Since nearly all normal objects are instances of object, this is broadly matching.
It is different from:
case object:which is a capture pattern named object, unless syntax resolves differently through context. Avoid ambiguous names in patterns.
37.40 Pattern Matching and None
To match None, use:
case None:This is a literal singleton pattern.
Do not use:
case x:and expect it to compare with an outer x.
Singleton patterns for None, True, and False are common and clear.
37.41 Pattern Matching and Security
Pattern matching may execute user code.
Potential execution points include:
equality comparison
attribute access
descriptor access
mapping methods
sequence methods
guard expressionsFor trusted in-memory objects, this is normal.
For untrusted objects with hostile methods, pattern matching should be treated like ordinary Python execution, not as a safe declarative query.
37.42 Inspecting Pattern Matching
Use dis to inspect match bytecode.
import dis
def f(value):
match value:
case [x, y]:
return x + y
case {"x": x}:
return x
case _:
return 0
dis.dis(f)Look for:
subject handling
pattern-specific checks
unpack operations
mapping operations
jumps between cases
stores for captured names
guard evaluation
return pathsThe exact bytecode changes across Python versions.
37.43 A Minimal Pattern Matcher
A tiny structural matcher for lists can show the idea.
def match_pair(value):
if isinstance(value, (list, tuple)) and len(value) == 2:
x, y = value
return True, {"x": x, "y": y}
return False, {}Use:
ok, binds = match_pair([1, 2])
if ok:
print(binds["x"] + binds["y"])This mirrors one small part of:
match value:
case [x, y]:
print(x + y)CPython does this through compiled bytecode and runtime helpers rather than a dictionary of bindings.
37.44 Common Misunderstandings
| Misunderstanding | Correct model |
|---|---|
match is only a switch statement | It performs structural matching and binding |
case x compares to variable x | It captures the subject into x |
_ is a normal capture | _ is a wildcard in patterns |
| The subject is evaluated for every case | It is evaluated once |
| Pattern matching is side-effect free | It can call equality, descriptors, guards, and attribute hooks |
| Failed matches are exceptions | Normal pattern failure falls through to the next case |
| Class patterns inspect fields directly | They use attribute access and __match_args__ |
Pattern matching is always faster than if | It still performs runtime checks |
37.45 Reading Strategy
Start with scalar cases:
def f(x):
match x:
case 0:
return "zero"
case _:
return "other"Then add structure:
def f(x):
match x:
case [a, b]:
return a + b
case {"name": name}:
return nameThen add classes:
class Point:
__match_args__ = ("x", "y")
def f(p):
match p:
case Point(0, y):
return y
case Point(x, y):
return x + yFor each version:
import dis
dis.dis(f)Track:
where the subject is stored
where each case begins
which checks can fail normally
which operations can raise
where names are bound
where guards run
where fallback jumps go37.46 Chapter Summary
Pattern matching is CPython’s structural conditional dispatch system. It evaluates a subject once, tries cases in order, checks patterns, binds names, evaluates guards, and executes the first matching body.
The core model is:
evaluate subject once
↓
try case pattern
↓
if pattern fails: clean up and try next case
↓
if pattern succeeds: bind names
↓
if guard exists: evaluate guard
↓
if guard true or absent: execute body