The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Match a literal | case 200: | View examples |
| Capture the subject | case value: | View examples |
| Ignore the subject | case _: | View examples |
| Match a fixed sequence | case [command, target]: | View examples |
| Capture remaining items | case [head, *middle, tail]: | View examples |
| Match a command prefix | case ['deploy', environment, *flags]: | View examples |
| Require mapping keys | case {'event': 'login', 'user': user}: | View examples |
| Capture extra mapping entries | case {'event': kind, **metadata}: | View examples |
| Match class attributes positionally | case Point(0, y): | View examples |
| Match class attributes by name | case Point(x=x, y=0): | View examples |
| Match alternatives | case 401 | 403: | View examples |
| Capture across alternatives | case ('get', path) | ('delete', path): | View examples |
| Keep a matched value | case (401 | 403) as status: | View examples |
| Add a guard | case {'score': score} if score >= 80: | View examples |
| Match a symbolic constant | case Status.READY: | View examples |
| Match nested structures | case {'type': 'move', 'point': [x, y]}: | View examples |
Structural pattern matching, available since Python 3.10, selects the first case whose pattern matches a subject and whose optional guard is true. Patterns describe shape and can bind parts of the subject; they are not ordinary Boolean conditions or assignment targets. Use match when several branches decompose the same structured value, and keep conventional if/elif logic when branches are primarily unrelated predicates.
Step by step
Detailed examples
Order specific cases before captures and wildcards
Cases are attempted from top to bottom and only the first qualifying block runs. Literal patterns test numbers and strings with equality, while None, True, and False use identity. A bare name is a capture pattern, not a constant lookup, and always succeeds; an unguarded capture or wildcard is therefore irrefutable and must be the final case. Successful bindings remain available after the match statement, but code must never depend on partial bindings from a failed match because their persistence is intentionally unspecified.
def classify(status):
match status:
case 200:
return 'ok'
case 400 | 404:
return 'client error'
case _:
return 'other'
for status in (200, 404, 503):
print(status, classify(status)) 200 ok
404 client error
503 otherDecompose sequences by length and position
Sequence patterns work with recognized sequence types, including list, tuple, range, and collections.abc.Sequence implementations, but not arbitrary iterators. They deliberately exclude str, bytes, and bytearray, preventing text from being split unexpectedly. Square and round pattern syntax behave alike. A fixed pattern requires an exact length, while one starred subpattern accepts any number of items and captures them in a list.
def parse(tokens):
match tokens:
case ['show']:
return 'show all'
case ['show', name]:
return f'show {name}'
case ['deploy', environment, *flags]:
return f'deploy {environment} with {len(flags)} flags'
case _:
return 'invalid'
print(parse(['show', 'cache']))
print(parse(['deploy', 'prod', '--force', '--wait']))
print(parse('show')) show cache
deploy prod with 2 flags
invalidSelect required mapping fields without requiring an exact shape
A mapping pattern succeeds when every listed key exists and its value matches; unrelated keys are ignored rather than causing failure. Add one final **rest capture when the remaining entries matter. Matching uses the mapping's two-argument get behavior rather than __missing__, so defaultdict and similar implicit insertion hooks are not triggered. Duplicate literal keys are a syntax error, and **_ is not permitted.
def route(event):
match event:
case {'event': 'login', 'user': user, **metadata}:
return f'login {user}; extras={sorted(metadata)}'
case {'event': kind}:
return f'event {kind}'
case _:
return 'invalid'
print(route({'event': 'login', 'user': 'Ada', 'ip': '127.0.0.1'}))
print(route({'event': 'logout', 'user': 'Ada'}))
print(route(['login', 'Ada'])) login Ada; extras=['ip']
event logout
invalidMatch typed objects through class attributes
A class pattern first applies isinstance to the subject. Keyword subpatterns read explicitly named attributes; missing attributes make the pattern fail, while other exceptions propagate. Positional subpatterns are translated through the class's __match_args__ tuple. Dataclasses and named tuples populate __match_args__ automatically, but keyword patterns are more resilient when a public constructor or field order may change.
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def location(point):
match point:
case Point(0, 0):
return 'origin'
case Point(x=x, y=0):
return f'x-axis at {x}'
case Point(x, y):
return f'point {x},{y}'
case _:
return 'not a point'
print(location(Point(0, 0)))
print(location(Point(7, 0)))
print(location(Point(2, 3))) origin
x-axis at 7
point 2,3Combine alternatives and retain the complete match
An OR pattern tries alternatives from left to right and stops at the first success. Every alternative must bind exactly the same set of names, which keeps later code unambiguous. An AS pattern binds the complete value after its left-hand pattern succeeds; parentheses clarify when AS applies to an entire OR pattern. These forms can remove duplicated case bodies while keeping supported shapes explicit.
def describe(request):
match request:
case ('get', path) | ('delete', path):
return f'resource {path}'
case ('status', (401 | 403) as code):
return f'denied {code}'
case _:
return 'unsupported'
print(describe(('get', '/health')))
print(describe(('delete', '/cache')))
print(describe(('status', 403))) resource /health
resource /cache
denied 403Use guards for constraints that shape alone cannot express
A guard runs only after its pattern has succeeded, so captured names are available in the guard expression. If the guard is false, matching continues with the next case; if it raises, the exception propagates. Guards are evaluated in case order until a block is selected. Keep them short and free of mutations because their side effects can make control flow difficult to reason about.
def grade(record):
match record:
case {'name': name, 'score': score} if score >= 80:
return f'{name}: pass'
case {'name': name, 'score': score} if 0 <= score < 80:
return f'{name}: retry'
case {'name': name, 'score': _}:
return f'{name}: invalid score'
case _:
return 'invalid record'
print(grade({'name': 'Mina', 'score': 91}))
print(grade({'name': 'Sol', 'score': 72}))
print(grade({'name': 'Ivo', 'score': -1})) Mina: pass
Sol: retry
Ivo: invalid scoreUse qualified names for constants in value patterns
A dotted name such as Status.READY is a value pattern: Python looks it up and compares the subject with that value. A bare READY would instead capture any subject and make following unguarded cases unreachable. Enums and namespaced constants make this distinction visible. Repeated value lookups within one match execution may be cached, so properties or descriptors used as constants should not rely on a precise lookup count.
from enum import Enum
class Status(Enum):
READY = 'ready'
BUSY = 'busy'
def message(status):
match status:
case Status.READY:
return 'start work'
case Status.BUSY:
return 'wait'
case _:
return 'unknown'
print(message(Status.READY))
print(message(Status.BUSY))
print(message('ready')) start work
wait
unknownCompose patterns to validate and unpack nested data
Patterns compose recursively, so one case can describe a mapping whose field contains a sequence or class instance. This is useful at a trusted parsing boundary after basic decoding, but it is not full schema validation: extra mapping keys are accepted, types only receive the checks expressed by subpatterns, and no matching case raises nothing automatically. Include a deliberate fallback that rejects or reports malformed input.
def interpret(message):
match message:
case {'type': 'move', 'point': [int(x), int(y)]}:
return f'move to {x},{y}'
case {'type': 'label', 'text': str(text)}:
return f'label {text}'
case _:
return 'invalid command'
print(interpret({'type': 'move', 'point': [4, 9]}))
print(interpret({'type': 'move', 'point': ['4', 9]}))
print(interpret({'type': 'label', 'text': 'home'})) move to 4,9
invalid command
label homeLocal code tester
Route structured messages
Change the messages, add a guarded case, or introduce a dataclass pattern to explore first-match behavior.
Press Run to load Python locally.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
- Python Software FoundationThe match statement — Python Language Referencedocs.python.org
- Python Software FoundationPEP 634 — Structural Pattern Matching: Specificationpeps.python.org
- Python Software FoundationPEP 635 — Structural Pattern Matching: Motivation and Rationalepeps.python.org
- Python Software FoundationPEP 636 — Structural Pattern Matching: Tutorialpeps.python.org
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



