- Published on
Today I Learnt: Python Structural Pattern Matching
- Authors

- Name
- Mehdi Akiki
Reference
Today I Learnt: Python Structural Pattern Matching
Python 3.10 introduced structural pattern matching with the match statement, bringing powerful functional programming patterns to Python. This feature allows for elegant and readable code when dealing with complex conditional logic.
The Basics
def process_data(data):
match data:
case {"type": "user", "name": name, "age": age}:
return f"User {name} is {age} years old"
case {"type": "product", "name": name, "price": price}:
return f"Product {name} costs ${price}"
case _:
return "Unknown data type"
Advanced Patterns
Value Patterns
def classify_number(n):
match n:
case 0:
return "zero"
case 1 | 2 | 3:
return "small number"
case x if x < 0:
return "negative"
case x if x % 2 == 0:
return "even"
case _:
return "odd positive"
Sequence Patterns
def analyze_list(items):
match items:
case []:
return "Empty list"
case [x]:
return f"Single item: {x}"
case [x, y]:
return f"Two items: {x}, {y}"
case [x, *rest]:
return f"First: {x}, others: {rest}"
case _:
return f"List with {len(items)} items"
Class Patterns
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
@dataclass
class Circle:
center: Point
radius: int
def describe_shape(shape):
match shape:
case Point(x=0, y=0):
return "Origin point"
case Point(x=x, y=y):
return f"Point at ({x}, {y})"
case Circle(center=Point(x=0, y=0), radius=r):
return f"Circle centered at origin with radius {r}"
case Circle(center=center, radius=radius):
return f"Circle with radius {radius}"
Real-World Use Cases
HTTP Request Handler
def handle_request(request):
match request:
case {"method": "GET", "path": "/users", "query": {"id": user_id}}:
return get_user(user_id)
case {"method": "POST", "path": "/users", "body": user_data}:
return create_user(user_data)
case {"method": "PUT", "path": path, "body": data} if path.startswith("/users/"):
user_id = path.split("/")[-1]
return update_user(user_id, data)
case {"method": method, "path": path}:
return {"error": f"Unsupported {method} {path}"}
Configuration Parser
def parse_config(config):
match config:
case {"database": {"type": "postgres", "host": host, "port": port}}:
return PostgresConnection(host, port)
case {"database": {"type": "mysql", "host": host, "port": port}}:
return MySQLConnection(host, port)
case {"cache": {"type": "redis", "host": host, "ttl": ttl}}:
return RedisCache(host, ttl)
case {"cache": {"type": "memory"}}:
return InMemoryCache()
case _:
raise ValueError("Invalid configuration")
Benefits
- Readability: Complex conditional logic becomes more readable
- Maintainability: Easier to add new cases without breaking existing code
- Type Safety: Better exhaustiveness checking
- Performance: Can be optimized by the Python interpreter
- Functional Programming: Brings FP patterns to Python
Gotchas
- Pattern matching is based on structure, not inheritance
- Order matters: more specific patterns should come before general ones
- Use
case _:for default cases to avoid NonExhaustiveMatchError - Guards (
ifconditions) can make patterns more specific
Pattern matching is a game-changer for Python developers, making code more expressive and maintainable. It's particularly powerful when dealing with complex data structures and conditional logic.