Skip to content

37. Pattern Matching

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 = None

Pattern 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 _:
        body

The 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 2

Only 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 succeeds

To 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 == literal

For 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 = x

This always matches.

Capture patterns are useful inside larger patterns:

match point:
    case [x, y]:
        result = x + y

Here, [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 = x

Invalid 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 subject

This 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 case

Guards 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 + y

This 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 names

37.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 = 4

The 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 = 37

Extra 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 = y

Then:

match p:
    case Point(x, y):
        ...

is conceptually similar to:

isinstance(p, Point)
x = p.x
y = p.y

with 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 y

Attribute 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 label

Nested 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 0

The 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 x

Here, 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 0

Conceptual bytecode structure:

load subject

try sequence case:
    check sequence
    check length 2
    unpack into x, y
    execute body
    return

fallback:
    return 0

CPython 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
fallback

Correct 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 false

Some failures are normal non-matches. Others are real exceptions.

Pattern matching must distinguish:

normal pattern failure
real runtime exception

For 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 exception

This 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 allowed

If **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 + y

Dataclasses 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.y

37.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: object

Then:

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 body

If 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 runs

Pattern 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 propagation

Some 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 branches

A 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 dispatch

Use 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 = value

If 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 x

The 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 True

This 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 True

or 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 expressions

For 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 paths

The 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

MisunderstandingCorrect model
match is only a switch statementIt performs structural matching and binding
case x compares to variable xIt captures the subject into x
_ is a normal capture_ is a wildcard in patterns
The subject is evaluated for every caseIt is evaluated once
Pattern matching is side-effect freeIt can call equality, descriptors, guards, and attribute hooks
Failed matches are exceptionsNormal pattern failure falls through to the next case
Class patterns inspect fields directlyThey use attribute access and __match_args__
Pattern matching is always faster than ifIt 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 name

Then 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 + y

For 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 go

37.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