Python Structural Pattern Matching
Learn Python match statements through literal, capture, sequence, mapping, class, value, wildcard, guard, OR, and AS patterns.
Intermediate
Python pattern matching lets you compare one subject against a series of patterns and run the first matching case. Patterns can also inspect the structure of sequences and mappings and bind matched values to names.
The basic match statement
A match statement evaluates its subject, then tries its case patterns in order. The first case whose pattern succeeds is selected. If no case matches, nothing runs unless you provide a final wildcard case.
status = 404
match status:
case 200:
message = "OK"
case 404:
message = "Not found"
case _:
message = "Other status"
print(message)Literal patterns and the wildcard
A literal pattern can match a value such as an integer or string. The underscore pattern, _, is a wildcard: it matches anything and does not bind a value to a name. Put a match-all case last because it cannot fail.
command = "stop"
match command:
case "start":
result = "Starting"
case "stop":
result = "Stopping"
case _:
result = "Unknown command"
print(result)Capture values from a sequence
Sequence patterns can match the shape of a sequence while capturing its elements into names. A starred name can capture the remaining items, similar to extended unpacking.
point = (4, 7)
match point:
case (x, y):
print(f"x={x}, y={y}")
case _:
print("Not a pair")Match mapping data
Mapping patterns match selected keys and can bind their values. Extra keys in the mapping do not prevent a mapping pattern from matching, so the pattern can focus on the fields it needs.
request = {"method": "GET", "path": "/home", "secure": True}
match request:
case {"method": "GET", "path": path}:
print(f"Read {path}")
case {"method": "POST", "path": path}:
print(f"Write {path}")
case _:
print("Other request")Use a guard for an extra condition
A case can include a guard after its pattern. The pattern must succeed first; only then is the guard expression evaluated. If the guard is false, Python continues to the next case.
number = 12
match number:
case n if n < 0:
label = "negative"
case n if n % 2 == 0:
label = "non-negative even"
case _:
label = "other"
print(label)Match alternatives with |
An OR pattern uses | to accept more than one pattern. Every alternative must bind the same set of names. This is useful when several shapes should lead to the same branch.
value = "yes"
match value:
case "yes" | "y":
print("accepted")
case "no" | "n":
print("rejected")
case _:
print("unknown")A bare name captures; a dotted name checks a value
A bare name such as READY is a capture pattern, even when it is written in uppercase. It succeeds for any subject and binds that subject to the name; it does not compare against a variable or constant that was defined earlier. To match a named value, use a dotted name such as State.READY or Color.RED. This distinction prevents a subtle bug: an unguarded capture pattern is irrefutable, so it can also make later case clauses unreachable.
Output
Ready to run.
Capture or compare?
Which pattern compares value with the existing State.READY value instead of binding a new name?
class State:
READY = "ready"
READY = State.READY
value = "stopped"
match value:
case :
print("matched")
print(READY, locals().get("state", "not bound"))
Match object attributes with a class pattern
A class pattern checks whether the subject is an instance of the named class and then matches the requested attributes. In Point(x=x, y=y), Point is the class, the left-hand x and y are attribute names, and the names on the right capture their values. You can add an AS pattern to keep the whole matched object as well: Point(x=x, y=y) as matched_point binds both coordinates and the original subject. Matching inspects the existing object; it does not create a new Point.
Output
Ready to run.
Recognize a class pattern
What does Point(x=x, y=y) test and bind when the subject is point?
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
class Circle:
def __init__(self, x, y):
self.x = x
self.y = y
point = Point(4, 7)
match point:
case :
print(locals().get("x", "not captured"), locals().get("y", "not captured"))
case _:
print("no match")
When match is a good fit
Use match when the decision naturally depends on the structure or shape of one subject. A simple if/elif chain can be clearer when you are only testing unrelated Boolean conditions. Pattern matching is not merely a switch statement: sequence and mapping patterns can inspect structure and bind values.
event = ["click", 120, 80]
match event:
case ["click", x, y]:
print(f"Clicked at {x}, {y}")
case ["keypress", key]:
print(f"Pressed {key}")
case _:
print("Other event")Key takeaways
- match evaluates one subject against case patterns in order and selects the first successful case.
- _ is a wildcard pattern that matches anything and does not bind a value.
- Sequence and mapping patterns can inspect structure while binding matched values to names.
- A guard is an if condition checked after its case pattern succeeds.
- Use match when structural patterns make the decision clearer; use ordinary conditionals when they are simpler.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.