Structural Pattern Matching Overview
π·οΈ Conditional Logic and Decision Making / The Match Statement
π§ Context Introduction
Python 3.10 introduced a powerful new feature called Structural Pattern Matching. Think of it as a supercharged version of the traditional if-elif-else chain. Instead of just checking if a value equals something, pattern matching lets you inspect the structure of your dataβchecking types, shapes, and nested values all at once. For engineers dealing with configuration files, API responses, or command parsing, this feature makes code cleaner, more readable, and less error-prone.
βοΈ What is Structural Pattern Matching?
At its core, structural pattern matching allows you to match a value against a series of patterns. A pattern can be:
- A simple literal value (like
42or"error") - A variable name that captures the value
- A type check (like
int(x)orstr(y)) - A sequence pattern (like
[a, b, c]) - A mapping pattern (like
{"key": value}) - A combination of the above with guards (extra conditions)
The syntax uses the keywords match and case instead of if and elif.
π οΈ Basic Syntax Breakdown
Here is the general structure of a match statement:
matchkeyword followed by the expression you want to evaluate- One or more
caseblocks, each with a pattern to match against - An optional
case _:as a wildcard (like a default/else clause) - Each case block can have an optional guard using
iffor extra conditions
A simple example without code blocks:
- match status_code:
- case 200: print("Success")
- case 404: print("Not Found")
- case 500: print("Server Error")
- case _: print("Unknown Status")
This replaces a traditional if status_code == 200: ... elif status_code == 404: ... chain.
π Comparison: Traditional If-Else vs. Match Statement
| Feature | Traditional If-Else | Match Statement |
|---|---|---|
| Readability | Can get messy with many conditions | Clean, visual alignment of cases |
| Type Checking | Requires isinstance() calls |
Built-in type patterns |
| Sequence Matching | Manual indexing and length checks | Automatic destructuring |
| Nested Data | Complex nested conditions | Natural structure matching |
| Performance | Linear evaluation | Optimized by Python interpreter |
| Default Case | Requires explicit else |
Built-in case _: wildcard |
π΅οΈ Common Use Cases for Engineers
1. Parsing Command-Line Arguments
Instead of manually splitting and checking sys.argv:
- match sys.argv:
- case ["script.py"]: run_interactive()
- case ["script.py", "--help"]: show_help()
- case ["script.py", "--config", path]: load_config(path)
- case _: print("Invalid arguments")
2. Handling API Response Structures
When dealing with JSON responses that can have different shapes:
- match response:
- case {"status": "ok", "data": data}: process_data(data)
- case {"status": "error", "message": msg}: log_error(msg)
- case {"status": "rate_limit", "retry_after": seconds}: wait_and_retry(seconds)
- case _: raise ValueError("Unexpected response format")
3. Processing Configuration Files
When config values can be different types:
- match config_value:
- case int(x): return x
- case str(x) if x.isdigit(): return int(x)
- case str(x): return x
- case list(items): return [process_item(i) for i in items]
- case _: raise TypeError(f"Unsupported config type: {type(config_value)}")
π― Advanced Patterns Worth Knowing
Sequence Patterns let you match lists or tuples by their structure:
- case [a, b, c]: matches exactly three elements
- case [first, *rest]: captures first element and remaining as a list
- case [*all]: captures the entire sequence
Mapping Patterns work with dictionaries:
- case {"name": name, "age": age}: matches dicts with those exact keys
- case {"name": name, rest}:** captures name and remaining key-value pairs
Class Patterns let you match against custom objects:
- case Point(x=0, y=0): matches a Point object at origin
- case HTTPError(status=code): captures the status code from an error object
Guards add extra conditions to a pattern:
- case int(x) if x > 0: matches only positive integers
- case str(s) if len(s) > 10: matches strings longer than 10 characters
β οΈ Important Notes and Gotchas
- Patterns are evaluated in order from top to bottom, just like if-elif
- The first matching case wins, so put more specific patterns first
- Variable names in patterns capture values, they don't compare against existing variables
- Use literal patterns (like
42or"hello") for exact matches - The wildcard
_is specialβit never binds to a variable name - Pattern matching works with any Python data type, not just built-ins
π¬ When to Use (and When Not to Use)
Use pattern matching when:
- You have multiple conditions checking the same variable
- You need to destructure complex nested data
- Your code involves type checking combined with value checking
- You're parsing structured input (commands, configs, API responses)
Avoid pattern matching when:
- You only have one or two simple conditions (use
if-else) - You need to check unrelated variables (use separate
ifstatements) - You're working with very large datasets (performance is comparable, but readability matters less)
- Your team is on Python 3.9 or earlier (pattern matching requires Python 3.10+)
π‘ Quick Tips for Beginners
- Start with simple literal matches before trying complex patterns
- Use the wildcard
case _:as your last case to catch unexpected values - Remember that patterns are about structure, not just values
- Practice by converting existing if-elif chains to match statements
- Use guards (
ifconditions) sparinglyβif you need many guards, consider if the pattern is right for your use case
Structural pattern matching is one of Python's most elegant features for writing clear, expressive code. Once you get comfortable with the basics, you'll find yourself reaching for it naturally whenever you need to make decisions based on the shape and content of your data.
Structural pattern matching lets you compare a value against multiple patterns and execute code based on which pattern matches.
π― Example 1: Matching a single value
This example shows how to match a variable against a specific literal value.
status = 200
match status:
case 200:
result = "OK"
case 404:
result = "Not Found"
print(result)
π€ Output: OK
π Example 2: Matching multiple values with OR pattern
This example demonstrates matching one value against several possible matches using the pipe operator.
error_code = 403
match error_code:
case 401 | 403:
message = "Access denied"
case 500:
message = "Server error"
print(message)
π€ Output: Access denied
π§© Example 3: Matching with a wildcard pattern
This example shows how to use an underscore as a catch-all for any unmatched value.
response_code = 302
match response_code:
case 200:
label = "Success"
case 404:
label = "Not Found"
case _:
label = "Other"
print(label)
π€ Output: Other
π¦ Example 4: Matching sequences (lists)
This example demonstrates matching a list against a pattern with specific elements and a variable for the rest.
command = ["move", "north", 5]
match command:
case ["move", direction, distance]:
action = f"Moving {direction} by {distance} units"
case ["stop"]:
action = "Stopping"
case _:
action = "Unknown command"
print(action)
π€ Output: Moving north by 5 units
π οΈ Example 5: Matching with guards (conditions)
This example shows how to add an if condition to a pattern for more specific matching.
point = (3, 4)
match point:
case (x, y) if x == y:
location = "On diagonal"
case (x, y) if x > 0 and y > 0:
location = "First quadrant"
case _:
location = "Other location"
print(location)
π€ Output: First quadrant
π Comparison: If-elif-else vs Match Statement
| Feature | If-elif-else | Match Statement |
|---|---|---|
| Syntax | Uses if, elif, else keywords |
Uses match, case keywords |
| Pattern types | Only boolean expressions | Supports literals, sequences, guards, wildcards |
| Readability | Good for simple conditions | Better for multiple distinct patterns |
| Performance | Evaluates each condition in order | Optimized for pattern matching |
| Use case | Any logical condition | Best when checking a single value against many patterns |
π§ Context Introduction
Python 3.10 introduced a powerful new feature called Structural Pattern Matching. Think of it as a supercharged version of the traditional if-elif-else chain. Instead of just checking if a value equals something, pattern matching lets you inspect the structure of your dataβchecking types, shapes, and nested values all at once. For engineers dealing with configuration files, API responses, or command parsing, this feature makes code cleaner, more readable, and less error-prone.
βοΈ What is Structural Pattern Matching?
At its core, structural pattern matching allows you to match a value against a series of patterns. A pattern can be:
- A simple literal value (like
42or"error") - A variable name that captures the value
- A type check (like
int(x)orstr(y)) - A sequence pattern (like
[a, b, c]) - A mapping pattern (like
{"key": value}) - A combination of the above with guards (extra conditions)
The syntax uses the keywords match and case instead of if and elif.
π οΈ Basic Syntax Breakdown
Here is the general structure of a match statement:
matchkeyword followed by the expression you want to evaluate- One or more
caseblocks, each with a pattern to match against - An optional
case _:as a wildcard (like a default/else clause) - Each case block can have an optional guard using
iffor extra conditions
A simple example without code blocks:
- match status_code:
- case 200: print("Success")
- case 404: print("Not Found")
- case 500: print("Server Error")
- case _: print("Unknown Status")
This replaces a traditional if status_code == 200: ... elif status_code == 404: ... chain.
π Comparison: Traditional If-Else vs. Match Statement
| Feature | Traditional If-Else | Match Statement |
|---|---|---|
| Readability | Can get messy with many conditions | Clean, visual alignment of cases |
| Type Checking | Requires isinstance() calls |
Built-in type patterns |
| Sequence Matching | Manual indexing and length checks | Automatic destructuring |
| Nested Data | Complex nested conditions | Natural structure matching |
| Performance | Linear evaluation | Optimized by Python interpreter |
| Default Case | Requires explicit else |
Built-in case _: wildcard |
π΅οΈ Common Use Cases for Engineers
1. Parsing Command-Line Arguments
Instead of manually splitting and checking sys.argv:
- match sys.argv:
- case ["script.py"]: run_interactive()
- case ["script.py", "--help"]: show_help()
- case ["script.py", "--config", path]: load_config(path)
- case _: print("Invalid arguments")
2. Handling API Response Structures
When dealing with JSON responses that can have different shapes:
- match response:
- case {"status": "ok", "data": data}: process_data(data)
- case {"status": "error", "message": msg}: log_error(msg)
- case {"status": "rate_limit", "retry_after": seconds}: wait_and_retry(seconds)
- case _: raise ValueError("Unexpected response format")
3. Processing Configuration Files
When config values can be different types:
- match config_value:
- case int(x): return x
- case str(x) if x.isdigit(): return int(x)
- case str(x): return x
- case list(items): return [process_item(i) for i in items]
- case _: raise TypeError(f"Unsupported config type: {type(config_value)}")
π― Advanced Patterns Worth Knowing
Sequence Patterns let you match lists or tuples by their structure:
- case [a, b, c]: matches exactly three elements
- case [first, *rest]: captures first element and remaining as a list
- case [*all]: captures the entire sequence
Mapping Patterns work with dictionaries:
- case {"name": name, "age": age}: matches dicts with those exact keys
- case {"name": name, rest}:** captures name and remaining key-value pairs
Class Patterns let you match against custom objects:
- case Point(x=0, y=0): matches a Point object at origin
- case HTTPError(status=code): captures the status code from an error object
Guards add extra conditions to a pattern:
- case int(x) if x > 0: matches only positive integers
- case str(s) if len(s) > 10: matches strings longer than 10 characters
β οΈ Important Notes and Gotchas
- Patterns are evaluated in order from top to bottom, just like if-elif
- The first matching case wins, so put more specific patterns first
- Variable names in patterns capture values, they don't compare against existing variables
- Use literal patterns (like
42or"hello") for exact matches - The wildcard
_is specialβit never binds to a variable name - Pattern matching works with any Python data type, not just built-ins
π¬ When to Use (and When Not to Use)
Use pattern matching when:
- You have multiple conditions checking the same variable
- You need to destructure complex nested data
- Your code involves type checking combined with value checking
- You're parsing structured input (commands, configs, API responses)
Avoid pattern matching when:
- You only have one or two simple conditions (use
if-else) - You need to check unrelated variables (use separate
ifstatements) - You're working with very large datasets (performance is comparable, but readability matters less)
- Your team is on Python 3.9 or earlier (pattern matching requires Python 3.10+)
π‘ Quick Tips for Beginners
- Start with simple literal matches before trying complex patterns
- Use the wildcard
case _:as your last case to catch unexpected values - Remember that patterns are about structure, not just values
- Practice by converting existing if-elif chains to match statements
- Use guards (
ifconditions) sparinglyβif you need many guards, consider if the pattern is right for your use case
Structural pattern matching is one of Python's most elegant features for writing clear, expressive code. Once you get comfortable with the basics, you'll find yourself reaching for it naturally whenever you need to make decisions based on the shape and content of your data.
Interactive Views
You are currently in π All-in-One mode. Use the tabs at the top to switch to π Theory Only or π» Code Only views.
Structural pattern matching lets you compare a value against multiple patterns and execute code based on which pattern matches.
π― Example 1: Matching a single value
This example shows how to match a variable against a specific literal value.
status = 200
match status:
case 200:
result = "OK"
case 404:
result = "Not Found"
print(result)
π€ Output: OK
π Example 2: Matching multiple values with OR pattern
This example demonstrates matching one value against several possible matches using the pipe operator.
error_code = 403
match error_code:
case 401 | 403:
message = "Access denied"
case 500:
message = "Server error"
print(message)
π€ Output: Access denied
π§© Example 3: Matching with a wildcard pattern
This example shows how to use an underscore as a catch-all for any unmatched value.
response_code = 302
match response_code:
case 200:
label = "Success"
case 404:
label = "Not Found"
case _:
label = "Other"
print(label)
π€ Output: Other
π¦ Example 4: Matching sequences (lists)
This example demonstrates matching a list against a pattern with specific elements and a variable for the rest.
command = ["move", "north", 5]
match command:
case ["move", direction, distance]:
action = f"Moving {direction} by {distance} units"
case ["stop"]:
action = "Stopping"
case _:
action = "Unknown command"
print(action)
π€ Output: Moving north by 5 units
π οΈ Example 5: Matching with guards (conditions)
This example shows how to add an if condition to a pattern for more specific matching.
point = (3, 4)
match point:
case (x, y) if x == y:
location = "On diagonal"
case (x, y) if x > 0 and y > 0:
location = "First quadrant"
case _:
location = "Other location"
print(location)
π€ Output: First quadrant
π Comparison: If-elif-else vs Match Statement
| Feature | If-elif-else | Match Statement |
|---|---|---|
| Syntax | Uses if, elif, else keywords |
Uses match, case keywords |
| Pattern types | Only boolean expressions | Supports literals, sequences, guards, wildcards |
| Readability | Good for simple conditions | Better for multiple distinct patterns |
| Performance | Evaluates each condition in order | Optimized for pattern matching |
| Use case | Any logical condition | Best when checking a single value against many patterns |