← Back to Course Index
Lesson 16 of 20

Pattern Matching & Enums

~20 min · match/case (Python 3.10+), enum.Enum, IntEnum, Flag, auto()

Ref
Primary Source
Official Docs — match statement

Also essential: enum module docs. PEP 634 has the full pattern matching spec if you want deep detail.

1 — Enums

Python's enum.Enum is your TypeScript enum or const union — a fixed set of named constants. Use them instead of magic strings or numbers.

TypeScript
// String union (preferred)
type Direction = "north" | "south" | "east" | "west";

// Enum
enum Status {
  Pending = "pending",
  Active = "active",
  Closed = "closed",
}

const s = Status.Active;
console.log(s);           // "active"
console.log(s === "active"); // true
Python
from enum import Enum, auto

class Direction(Enum):
    NORTH = "north"
    SOUTH = "south"
    EAST  = "east"
    WEST  = "west"

class Status(Enum):
    PENDING = "pending"
    ACTIVE  = "active"
    CLOSED  = "closed"

s = Status.ACTIVE
print(s)           # Status.ACTIVE
print(s.value)     # "active"
print(s.name)      # "ACTIVE"
print(s == Status.ACTIVE)  # True
print(s == "active")       # False! Enum ≠ its value
# auto() — auto-assign integer values
class Color(Enum):
    RED   = auto()   # 1
    GREEN = auto()   # 2
    BLUE  = auto()   # 3

# Iterate members
for direction in Direction:
    print(direction.name, direction.value)

# Look up by value
Status("active")    # Status.ACTIVE
Status["ACTIVE"]    # Status.ACTIVE  ← by name

# Membership check
"active" in [s.value for s in Status]  # True

# IntEnum — comparisons work with plain ints
from enum import IntEnum
class Priority(IntEnum):
    LOW    = 1
    MEDIUM = 2
    HIGH   = 3

Priority.HIGH > Priority.LOW   # True
Priority.HIGH > 2              # True  ← int comparison works

# Flag — bitwise combination (like Unix permissions)
from enum import Flag
class Permission(Flag):
    READ    = auto()
    WRITE   = auto()
    EXECUTE = auto()
    ALL     = READ | WRITE | EXECUTE

perm = Permission.READ | Permission.WRITE
Permission.READ in perm   # True

2 — match/case — Structural Pattern Matching

Python 3.10+ finally has a switch statement — but far more powerful. It matches structure, not just equality.

JavaScript — switch
switch (status) {
  case "pending":
    handlePending();
    break;
  case "active":
    handleActive();
    break;
  default:
    throw new Error("Unknown status");
}
Python — match/case
match status:
    case "pending":
        handle_pending()
    case "active":
        handle_active()
    case _:            # default / wildcard
        raise ValueError(f"Unknown: {status}")

# No break needed — cases don't fall through

3 — Pattern Types

match/case goes far beyond switch — it can destructure sequences, dicts, and classes.

Literal patterns

match command:
    case "quit":
        sys.exit(0)
    case "help":
        print_help()
    case "version":
        print("1.0.0")

OR patterns with |

match status_code:
    case 200 | 201 | 204:
        print("Success")
    case 400 | 422:
        print("Client error")
    case 500 | 502 | 503:
        print("Server error")

Sequence patterns — destructuring

point = (3, 4)

match point:
    case (0, 0):
        print("Origin")
    case (x, 0):
        print(f"On x-axis at {x}")    # x is captured!
    case (0, y):
        print(f"On y-axis at {y}")
    case (x, y):
        print(f"At ({x}, {y})")       # both captured

Mapping (dict) patterns

def handle_event(event: dict):
    match event:
        case {"type": "click", "x": x, "y": y}:
            print(f"Click at ({x}, {y})")
        case {"type": "keydown", "key": key}:
            print(f"Key pressed: {key}")
        case {"type": t}:
            print(f"Unknown event type: {t}")

Class patterns

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

@dataclass
class Circle:
    center: Point
    radius: float

def describe(shape):
    match shape:
        case Circle(center=Point(x=0, y=0), radius=r):
            print(f"Circle at origin, radius {r}")
        case Circle(center=Point(x=x, y=y), radius=r):
            print(f"Circle at ({x},{y}), radius {r}")
        case _:
            print("Unknown shape")

Guard clauses with if

match value:
    case x if x < 0:
        print(f"Negative: {x}")
    case x if x == 0:
        print("Zero")
    case x if x > 0:
        print(f"Positive: {x}")

4 — match + Enum (the killer combo)

from enum import Enum

class Direction(Enum):
    NORTH = "north"
    SOUTH = "south"
    EAST  = "east"
    WEST  = "west"

def move(direction: Direction, steps: int = 1):
    match direction:
        case Direction.NORTH:
            y += steps
        case Direction.SOUTH:
            y -= steps
        case Direction.EAST:
            x += steps
        case Direction.WEST:
            x -= steps

🧠 Quiz

1. Status.ACTIVE == "active" when Status is an Enum?

2. In a match/case, does Python fall through to the next case like JavaScript's switch?

3. In case (x, 0):, what does x do?

4. What does Permission.READ | Permission.WRITE produce with Flag?

0/4

Questions answered correctly.