Structural Pattern Matching
Branch cleanly on complex data shapes using match-case statements, sequence patterns, and guard clauses.
Matching Shape, Not Just Value
match subject: followed by case pattern: blocks checks the subject against each pattern in order and runs the first one that matches — there is no C-style fall-through, so unlike a switch statement, you never need a break.
A pattern isn't just a value comparison — it can destructure a list's head and tail, pull specific keys out of a dict while ignoring the rest, or match an object's type and then bind its attributes to new names, all in one line.
t, x, y = e["target"], e["x"], e["y"]
...
match/case is like a customs officer checking shipping manifests: instead of asking one question at a time (“is it a box? is it heavy? does it have a label?”), they check the whole shape of the shipment against known templates at once and route it accordingly.
Literal and Or-Patterns with a Wildcard
Run the snippet below to see how the pipe operator groups literals and _ catches everything else:
def http_status_category(status_code):
match status_code:
case 200 | 201 | 204:
return "SUCCESS"
case 301 | 302 | 304:
return "REDIRECT"
case 400 | 401 | 403 | 404:
return "CLIENT_ERROR"
case 500 | 502 | 503:
return "SERVER_ERROR"
case _:
return "UNKNOWN_STATUS"
print("200:", http_status_category(200))
print("404:", http_status_category(404))
print("999:", http_status_category(999))Groups several literal values into one case without repeating the block body.
[first, *rest] binds the head to first and every remaining element to rest as a list.
A dict pattern only requires the keys it names — extra keys in the real dict are silently ignored.
Names bound inside a matched case remain available after the match block ends, in the enclosing function scope.
Five Pattern Types, Side by Side
Click a pattern type to see its exact syntax and what makes it distinct:
The pipe operator (|) combines multiple literal values into a single case — matches if the subject equals any of them.
Positional vs Keyword Class Matching, Plus Guards
__match_args__ enables compact positional destructuring for domain objects; guard clauses add one more runtime condition before a case commits.
Positional class matching
__match_args__ = ("order_id", "amount", "status")Declares which attributes map to positional slots in a class pattern.
case Order(oid, amt, status):Positional form — relies entirely on the __match_args__ tuple order.
Keyword class matching
case Order(amount=amt, status="cancelled"):Named form — works on any class, no __match_args__ required.
Guard clauses
case Order(amount=amt) if amt > 1000:An if condition after the pattern — only commits to this case if both the shape AND the guard pass.
case _ if is_holiday(today):Guards can reference any bound variable or outer-scope value.
Worked Hands-on Walkthroughs
Progress step-by-step from literal or-patterns through event-payload destructuring to full class pattern matching:
Basic Match-Case with Literal and Or-Patterns
The pipe operator (|) combines multiple literal values into an or-pattern, while the wildcard (_) acts as an exhaustive catch-all.
def http_status_category(status_code):
match status_code:
case 200 | 201 | 204:
return "SUCCESS"
case 301 | 302 | 304:
return "REDIRECT"
case 400 | 401 | 403 | 404:
return "CLIENT_ERROR"
case 500 | 502 | 503:
return "SERVER_ERROR"
case _:
return "UNKNOWN_STATUS"
print("200:", http_status_category(200))
print("404:", http_status_category(404))
print("999:", http_status_category(999))The pipe operator (|) combines multiple literal values into an or-pattern, while the wildcard (_) acts as an exhaustive catch-all.
Destructuring Commands and Event Payloads
Mapping patterns match the required 'type', 'target', 'x', and 'y' keys while gracefully ignoring the extra 'timestamp' key.
def process_event(event):
match event:
# Sequence unpacking with head and tail
case ["NAVIGATE", str(url), *options]:
return f"Navigate to {url} with {len(options)} options"
# Mapping pattern with partial key matching
case {"type": "CLICK", "target": target, "x": int(x), "y": int(y)}:
return f"Click on {target} at ({x}, {y})"
case {"type": "KEYPRESS", "key": key}:
return f"Keypress: {key}"
case _:
return "Unrecognized event payload"
# 1. Sequence match
print(process_event(["NAVIGATE", "https://pylabs.dev", "--headless", "--incognito"]))
# 2. Dictionary match with extra ignored keys
click_data = {"type": "CLICK", "target": "submit_btn", "x": 120, "y": 340, "timestamp": 1670000000}
print(process_event(click_data))Mapping patterns match the required 'type', 'target', 'x', and 'y' keys while gracefully ignoring the extra 'timestamp' key.
Class Patterns with Positional and Keyword Matching
__match_args__ enables expressive, compact positional pattern destructuring for domain objects.
class Order:
__match_args__ = ("order_id", "amount", "status")
def __init__(self, order_id, amount, status="pending"):
self.order_id = order_id
self.amount = amount
self.status = status
def audit_order(item):
match item:
# Positional matching enabled by __match_args__
case Order(oid, amt, "cancelled"):
return f"Order #{oid} cancelled (Voided ${amt})"
# Keyword matching with guard
case Order(order_id=oid, amount=amt) if amt >= 1000:
return f"High-value Order #{oid}: ${amt} - Requires Approval"
case Order(oid, amt, status):
return f"Standard Order #{oid}: ${amt} [{status}]"
case _:
return f"Invalid entity type: {type(item).__name__}"
print(audit_order(Order(101, 1500, "active")))
print(audit_order(Order(102, 50, "cancelled")))
print(audit_order(Order(103, 75, "shipped")))
print(audit_order("invalid_data"))__match_args__ enables expressive, compact positional pattern destructuring for domain objects.
Common Traps & How to Avoid Them
Study the mistakes and clean fixes below before writing production match-case logic:
# Mistyped variable or missing closing parenthesis
result = structural_pattern_matching__match_case__val# Declare and verify variables before execution
val = 10
print("Defined correctly:", val)Beginners frequently encounter typos or forgotten definitions before using an identifier.
# Modifying shared state or unexpected variable shadowing
def update_state(data=[]):
data.append(1)
return datadef update_state(data=None):
if data is None:
data = []
data.append(1)
return dataDefault arguments in Python are evaluated once at function definition time, not at invocation time.
# Opening resources in a tight loop without closing or bounding memory
files = [open(f"log_{i}.txt", "w") for i in range(5000)]for i in range(5):
with open(f"log_{i}.txt", "w") as f:
f.write("Safe")Failing to release system resources leads to process termination under heavy production workloads.
Real-World Systems in Action
See how scripts, microservices, and compilers rely on structural matching to replace deep if-elif chains:
Local Developer Automation Tool
Individual developers and IT administrators automate everyday repetitive workflows.
A clean standalone script providing fast, local automation with zero dependencies.
command = "QUIT"
match command:
case "START":
print("Starting service...")
case "QUIT":
print("Shutting down cleanly.")
case _:
print("Unknown command.")Backend API Service Integration
SaaS platforms run backend microservices processing requests concurrently.
A modular service handler integrating validation and structured responses.
# Service integration layer
def handle_request(payload: dict) -> dict:
return {"status": "success", "processed": True}High-Throughput Cloud Architecture
Compilers, AST analyzers, and protocol deserializers replace complex nested if-elif trees with elegant match-case dispatch.
Compilers, AST analyzers, and protocol deserializers replace complex nested if-elif trees with elegant match-case dispatch.
# High-resilience production pattern
import time
def process_stream(events: list) -> int:
processed = 0
for event in events:
if event:
processed += 1
return processedKey Points & Concept Review
- match-case is a structural destructuring system with no C-style fall-through behavior.
- Sequence patterns ([first, *rest]) allow head/tail destructuring and fixed-length matching.
- Mapping patterns (dictionaries) perform open matching: extra unmentioned keys are ignored.
- Guard clauses (case pattern if condition:) allow additional runtime checks before committing to a case.
- Variables bound inside case statements remain available in the enclosing function scope.
- Custom classes enable positional pattern matching by defining the __match_args__ class tuple.
Frequently Asked Questions & Reference Hub
match-case is one of Python's newer features — no worries if sequence and mapping patterns feel unfamiliar at first; once you refactor one nested if-elif chain into a match block, the shape-matching mental model clicks fast.
Read the specification that introduced match-case to Python.
See more examples of destructuring lists and dicts in case blocks.
Learn exactly how classes opt into positional pattern matching.
Understand when a guard clause runs relative to pattern matching.
Compare match-case readability against traditional branching.
See how compilers and parsers apply structural matching.
You've Mastered Structural Pattern Matching!
You can write literal, sequence, mapping, and class patterns with guard clauses. Now lock in your knowledge with hands-on practice — and take on the Module 08 final exam.
Write a match-case block with sequence and class patterns.
Earn experience points toward your developer rank as soon as you pass.
Bite-sized knowledge check with instant explanations for every question.
Worked example
Route a command using structural pattern matching.
match ["quit"]:
case ["quit"]: print("Bye")
How it works, step by step
- match/case inspects sequences and matches patterns.
Structural Pattern Matching practice
Write a function parse_action(data) using match/case that inspects a dictionary. If {"action": "ping"}, return "pong". If {"action": "greet", "name": name}, return f"Hello, {name}". For anything else, return "unknown". Test with parse_action({"action": "greet", "name": "Sam"}) and print the result: Hello, Sam.
Coding Practice is Locked
Sign in to access the interactive Python editor, write and run code checks against automated tests, use hints, and earn XP.
Practice quiz
Check yourself
15 questions with explanations after submission, so you can correct course as you go.
Structural Pattern Matching quiz
LockedYou must be signed in to take this quiz. Sign in to test your understanding, track your answers, and earn XP.
Lesson incomplete
Not completedCreate an ID or sign in to save lesson completion across devices.