Skip to main content Brad's PyNotes

PEP 622 Pattern Matching

TL;DR

PEP 622 introduced match-case statements for structural pattern matching, enabling elegant destructuring and dispatch based on data shape and values.

Interesting!

Pattern matching brings functional programming elegance to Python - you can destructure complex nested data in a single line and handle different cases cleanly!

Basic Match Statement

python code snippet start

def handle_data(data):
    match data:
        case 0:
            return "zero"
        case 1 | 2 | 3:
            return "small number"
        case int(x) if x > 100:
            return f"large number: {x}"
        case str(s) if len(s) < 5:
            return f"short string: {s}"
        case _:
            return "something else"

print(handle_data(0))      # zero
print(handle_data(150))    # large number: 150
print(handle_data("hi"))   # short string: hi

python code snippet end

Sequence Patterns

python code snippet start

def analyze_list(items):
    match items:
        case []:
            return "empty list"
        case [x]:
            return f"single item: {x}"
        case [x, y]:
            return f"pair: {x}, {y}"
        case [x, *rest]:
            return f"starts with {x}, has {len(rest)} more"
        case _:
            return "not a list"

print(analyze_list([]))           # empty list
print(analyze_list([1, 2, 3, 4])) # starts with 1, has 3 more

python code snippet end

Mapping Patterns

python code snippet start

def process_request(request):
    match request:
        case {"action": "get", "resource": resource}:
            return f"Getting {resource}"
        case {"action": "post", "resource": resource, "data": data}:
            return f"Posting to {resource}: {data}"
        case {"action": "delete", "resource": resource, "confirm": True}:
            return f"Deleting {resource}"
        case {"action": "delete", "resource": resource}:
            return f"Delete {resource} requires confirmation"
        case _:
            return "Invalid request"

request1 = {"action": "get", "resource": "users"}
print(process_request(request1))  # Getting users

python code snippet end

Class Patterns

python code snippet start

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

@dataclass 
class Circle:
    center: Point
    radius: float

def describe_shape(shape):
    match shape:
        case Point(0, 0):
            return "origin point"
        case Point(x, 0):
            return f"point on x-axis at {x}"
        case Point(0, y):
            return f"point on y-axis at {y}"
        case Point(x, y):
            return f"point at ({x}, {y})"
        case Circle(Point(0, 0), radius):
            return f"circle at origin with radius {radius}"
        case Circle(center, radius):
            return f"circle at {center} with radius {radius}"
        case _:
            return "unknown shape"

shapes = [
    Point(0, 0),
    Point(3, 0),
    Circle(Point(1, 2), 5)
]

for shape in shapes:
    print(describe_shape(shape))

python code snippet end

Complex Nested Patterns

python code snippet start

def parse_expression(expr):
    match expr:
        case {"type": "number", "value": n}:
            return n
        case {"type": "add", "left": left, "right": right}:
            return parse_expression(left) + parse_expression(right)
        case {"type": "multiply", "left": left, "right": right}:
            return parse_expression(left) * parse_expression(right)
        case {"type": "variable", "name": name}:
            return f"var_{name}"
        case _:
            return "invalid expression"

# Parse: (2 + 3) * 4
expr = {
    "type": "multiply",
    "left": {
        "type": "add",
        "left": {"type": "number", "value": 2},
        "right": {"type": "number", "value": 3}
    },
    "right": {"type": "number", "value": 4}
}

print(parse_expression(expr))  # 20

python code snippet end

Guards and Conditions

python code snippet start

def categorize_triangle(sides):
    match sides:
        case [a, b, c] if a == b == c:
            return "equilateral"
        case [a, b, c] if a == b or b == c or a == c:
            return "isosceles"  
        case [a, b, c] if a + b > c and b + c > a and a + c > b:
            return "scalene"
        case [a, b, c] if a**2 + b**2 == c**2:
            return "right triangle"
        case _:
            return "not a valid triangle"

print(categorize_triangle([3, 3, 3]))  # equilateral
print(categorize_triangle([3, 4, 5]))  # right triangle

python code snippet end

Pattern matching transforms complex conditional logic into readable, expressive code - especially powerful for parsing, data processing, and type-based dispatch!

Pattern matching works beautifully with control flow structures and Python's data structures for elegant code organization.

Reference: PEP 622 – Structural Pattern Matching