Python - Structural Pattern Matching (match-case) in Python 3.10+
Structural Pattern Matching is a feature introduced in Python 3.10 that provides a powerful and readable way to compare a value against multiple patterns. It is implemented using the match and case keywords and serves as an advanced alternative to long chains of if-elif-else statements. Unlike traditional conditional statements that compare only values, structural pattern matching can also inspect the structure of data such as lists, tuples, dictionaries, classes, and nested objects.
This feature makes code cleaner, easier to understand, and more maintainable, especially when working with complex data structures or parsing different types of input.
Why Structural Pattern Matching is Important
Before Python 3.10, developers often relied on multiple nested if-elif-else statements to handle different conditions. As applications grew larger, these conditions became difficult to manage and increased the chances of errors.
Structural pattern matching solves this problem by allowing developers to describe the expected structure of data directly. The interpreter automatically checks whether the data matches the specified pattern and executes the corresponding block.
Benefits include:
-
Improved code readability.
-
Reduced complexity in conditional logic.
-
Easier handling of nested data structures.
-
Better organization of multiple decision branches.
-
Cleaner code for parsing configuration files, JSON responses, and user commands.
Basic Syntax
The general syntax is:
match expression:
case pattern1:
# code
case pattern2:
# code
case _:
# default case
The match statement evaluates an expression.
Each case specifies a pattern.
The underscore (_) acts as a wildcard and matches anything if no previous case succeeds.
Simple Value Matching
day = 3
match day:
case 1:
print("Monday")
case 2:
print("Tuesday")
case 3:
print("Wednesday")
case _:
print("Invalid day")
Output:
Wednesday
Here, Python compares the value of day with each case until it finds a match.
Matching Multiple Values
You can match several values in one case using the | operator.
grade = "A"
match grade:
case "A" | "A+":
print("Excellent")
case "B":
print("Good")
case "C":
print("Average")
case _:
print("Needs Improvement")
Output:
Excellent
This is similar to using logical OR but is more concise and readable.
Sequence Pattern Matching
Lists and tuples can be matched based on both their length and contents.
numbers = [5, 10]
match numbers:
case [a, b]:
print(a)
print(b)
Output:
5
10
Python automatically assigns the values to variables.
If the sequence length does not match, the pattern fails.
Matching Lists with Variable Length
The * operator captures remaining elements.
data = [10, 20, 30, 40, 50]
match data:
case [first, *middle, last]:
print(first)
print(middle)
print(last)
Output:
10
[20, 30, 40]
50
This is useful when processing lists of unknown length.
Tuple Matching
point = (4, 7)
match point:
case (0, 0):
print("Origin")
case (0, y):
print("Y-axis")
case (x, 0):
print("X-axis")
case (x, y):
print("Point:", x, y)
Output:
Point: 4 7
Python checks each tuple pattern in order.
Dictionary Pattern Matching
Dictionaries can also be matched based on their keys.
student = {
"name": "Alice",
"age": 20
}
match student:
case {"name": name, "age": age}:
print(name)
print(age)
Output:
Alice
20
Extra dictionary keys are ignored unless explicitly matched.
Nested Pattern Matching
Complex nested structures can be matched directly.
record = {
"student": {
"name": "John",
"marks": 90
}
}
match record:
case {
"student": {
"name": name,
"marks": marks
}
}:
print(name)
print(marks)
Output:
John
90
This eliminates multiple dictionary lookups.
Using Guards
A guard adds an extra condition using if.
age = 19
match age:
case x if x >= 18:
print("Adult")
case _:
print("Minor")
Output:
Adult
The value must match both the pattern and the guard condition.
Matching Class Objects
Custom objects can also participate in structural pattern matching.
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
emp = Employee("David", 60000)
match emp:
case Employee(name, salary):
print(name)
print(salary)
This makes object inspection much simpler than manually checking attributes.
Wildcard Pattern
The underscore (_) is a wildcard that matches any remaining values.
status = "Pending"
match status:
case "Completed":
print("Done")
case "Processing":
print("Working")
case _:
print("Unknown Status")
Output:
Unknown Status
The wildcard should generally appear as the last case because it catches all unmatched values.
Capturing Values
Patterns can capture values into variables.
color = "Blue"
match color:
case value:
print("Selected:", value)
Output:
Selected: Blue
Instead of comparing, the pattern stores the matched value in a variable.
Real-World Applications
Structural pattern matching is especially useful in modern Python applications, including:
-
Parsing JSON data received from APIs.
-
Processing user commands in command-line applications.
-
Building interpreters and compilers.
-
Handling network protocol messages.
-
Routing requests in web applications.
-
Parsing configuration files.
-
Processing XML and YAML data.
-
Data validation in automation scripts.
-
Event-driven programming.
-
Workflow engines and state machines.
Advantages
-
Produces cleaner and more organized code.
-
Reduces lengthy
if-elif-elsechains. -
Simplifies handling of complex and nested data.
-
Improves readability and maintainability.
-
Supports lists, tuples, dictionaries, objects, and nested structures.
-
Allows combining patterns with guard conditions.
-
Encourages a declarative programming style where the shape of data is expressed directly.
Limitations
-
Available only in Python 3.10 and later.
-
May be unfamiliar to developers accustomed to older Python versions.
-
Overly complex patterns can reduce readability if not used carefully.
-
Pattern matching is intended for structural comparisons and should not replace simple equality checks where a basic
ifstatement is clearer.
Best Practices
-
Use structural pattern matching when handling multiple related cases or complex data structures.
-
Keep each
caseconcise and focused on a single pattern. -
Place more specific patterns before general ones.
-
Use the wildcard (
_) as the final fallback case. -
Apply guards only when additional conditions are necessary.
-
Test all possible input structures to ensure patterns behave as expected.
-
Use meaningful variable names when capturing values from matched patterns.
Conclusion
Structural Pattern Matching is one of the most significant additions to modern Python. It enables developers to write concise, expressive, and maintainable code by matching the structure of data rather than relying on lengthy conditional statements. With support for values, sequences, dictionaries, objects, nested structures, and conditional guards, it simplifies many common programming tasks and is particularly valuable in applications that process structured or hierarchical data. Mastering this feature allows Python developers to write cleaner, more scalable, and more readable programs using modern language capabilities.