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 last case is a wildcard that will match anything. It acts like the default case in a switch 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.