Skip to content

Pattern Matching Intermediate

🐍 Core Python Track · Level 3
⏱️ ~3 days 📚 Prerequisite: Control Flow

match/case (Python 3.10+, PEP 634) is structural pattern matching — it matches the shape of data and can bind parts of it to names. It is far more than a C-style switch.


Literal matching

Literal matching in Pattern Matching — what it is and when to use it.

def http_label(code):
    match code:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500:
            return "Server Error"
        case _:                     # wildcard — the "default"
            return "Unknown"

print(http_label(200))   # OK
print(http_label(999))   # Unknown

Or-patterns and value capture

Or-patterns and value capture in Pattern Matching — what it is and when to use it.

def kind(ch):
    match ch:
        case "a" | "e" | "i" | "o" | "u":
            return "vowel"
        case letter if letter.isalpha():
            return "consonant"
        case _:
            return "other"

print(kind("e"))   # vowel
print(kind("z"))   # consonant
print(kind("7"))   # other

Sequence patterns (with capture & star)

Sequence patterns (with capture & star) in Pattern Matching — what it is and when to use it.

def describe(seq):
    match seq:
        case []:
            return "empty"
        case [x]:
            return f"one: {x}"
        case [first, *rest]:
            return f"first={first}, rest={rest}"

print(describe([]))          # empty
print(describe([42]))        # one: 42
print(describe([1, 2, 3]))   # first=1, rest=[2, 3]

Mapping patterns

Mapping patterns in Pattern Matching — what it is and when to use it.

Match specific keys in a dict; extra keys are ignored.

def route(event):
    match event:
        case {"type": "click", "x": x, "y": y}:
            return f"click at ({x}, {y})"
        case {"type": "key", "key": k}:
            return f"key {k}"
        case _:
            return "unhandled"

print(route({"type": "click", "x": 10, "y": 20}))   # click at (10, 20)
print(route({"type": "key", "key": "Enter"}))        # key Enter

Class patterns

Class patterns in Pattern Matching — what it is and when to use it.

Destructure objects by type and attributes. Dataclasses work especially well.

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

def quadrant(p):
    match p:
        case Point(x=0, y=0):
            return "origin"
        case Point(x=0):
            return "on y-axis"
        case Point(y=0):
            return "on x-axis"
        case Point(x=x, y=y) if x > 0 and y > 0:
            return "Q1"
        case _:
            return "elsewhere"

print(quadrant(Point(0, 0)))   # origin
print(quadrant(Point(3, 4)))   # Q1
print(quadrant(Point(0, 5)))   # on y-axis

Guards

Guards in Pattern Matching — what it is and when to use it.

A if after a pattern adds an extra condition that must also hold:

def classify(n):
    match n:
        case int() if n < 0:
            return "negative int"
        case int():
            return "non-negative int"
        case _:
            return "not an int"

print(classify(-3))     # negative int
print(classify(5))      # non-negative int
print(classify("x"))    # not an int

When to reach for match

Use match when you branch on the structure of data (parsing, ASTs, event handling, command dispatch). For a simple value lookup, a dict is still cleaner than a match.


Practice exercises

  1. Write a match that parses a ["move", x, y] / ["rotate", deg] command list.
  2. Match an HTTP response dict and extract status and body, defaulting body to "".
  3. Destructure a nested point Point(Point(0,0), Point(x,y)) with class patterns.
  4. Use a guard to match only even integers.

💬 Discussion

Have a question about this topic? Found an error? Share your thoughts below.