Python's New match Statement: A Guide to Structural Pattern Matching
An introduction to one of the biggest new features in Python 3.10: structural pattern matching. Learn how to use the new match/case statement to write cleaner, more expressive conditional logic.
For years, developers coming to Python from other languages have asked, "Where is the switch/case
statement?" The traditional answer has been to use a series of if/elif/else
blocks or a dictionary of functions. With the release of Python 3.10, there is a new and much more powerful answer: structural pattern matching, introduced with the match
and case
keywords.
But this is not just a simple switch/case
. It's a powerful tool for matching against the structure of your data.
The Basic match/case
At its simplest, match/case
can work like a switch
statement.
def http_status(status):
match status:
case 200:
return "OK"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
case _:
return "Unknown Status"
print(http_status(404)) # Output: Not Found
- The
_
in the lastcase
is a wildcard that will match anything. It acts like thedefault
case in aswitch
statement.
The Power: Structural Matching
The real power of match
comes from its ability to match against the structure of objects like tuples, lists, and dictionaries.
Matching Tuples and Lists
You can match against the shape of a sequence and bind values to variables.
Example: Processing commands
def process_command(command):
match command:
case ["move", x, y]:
print(f"Moving to ({x}, {y})")
case ["draw", "circle", radius]:
print(f"Drawing a circle with radius {radius}")
case ["draw", "line", p1, p2]:
print(f"Drawing a line from {p1} to {p2}")
case ["quit"]:
print("Quitting...")
case _:
print("Invalid command")
process_command(["move", 10, 20])
process_command(["draw", "circle", 5])
This is far more expressive than a series of if
statements checking the length of the list and the value of its elements.
Matching Dictionaries
You can match against the presence of specific keys in a dictionary.
def process_data(data):
match data:
case {"type": "user", "name": name, "email": email}:
print(f"Processing user: {name} <{email}>")
case {"type": "order", "id": order_id}:
print(f"Processing order: {order_id}")
case _:
print("Unknown data format")
process_data({"type": "user", "name": "Alice", "email": "alice@example.com"})
Combining Patterns
You can use the |
(or) operator to combine several patterns into a single case.
match command:
case "north" | "south" | "east" | "west":
print("Moving...")
Adding if
Guards
You can add an if
condition to a case
statement to provide more specific matching logic. This is called a guard.
def process_point(point):
match point:
case (x, y) if x == 0 and y == 0:
print("At the origin")
case (x, y) if x == 0:
print(f"On the Y-axis at {y}")
case (x, y) if y == 0:
print(f"On the X-axis at {x}")
case (x, y):
print(f"At ({x}, {y})")
process_point((0, 5))
Matching Against Objects
You can even match against the attributes of your own custom objects.
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def process_point_object(point):
match point:
case Point(x=0, y=0):
print("At the origin")
case Point(x=x, y=0):
print(f"On the X-axis at {x}")
case Point(x=0, y=y):
print(f"On the Y-axis at {y}")
case Point(x, y):
print(f"At ({x}, {y})")
process_point_object(Point(5, 0))
Conclusion
Structural pattern matching is one of the most significant syntax additions in the history of Python. It goes far beyond a simple switch/case
, providing a powerful and expressive way to control the flow of your program based on the shape and value of your data. By replacing complex and nested if/elif/else
chains with clean match/case
blocks, you can make your code more readable, less error-prone, and more declarative.