DMarlon Studio

Lesson 12 · 12 of 20

Enums and match/case · Python

Order states with Enum and events handled with match/case in Python: value, mapping and sequence patterns, captures and guards.

Python · Enums and match/case in DMarlon Studio: the example's code and its output in the terminal

An enumeration (Enum) groups a closed set of named values. Status.PAID is much safer than the string "paid": a typo such as Status.PIAD fails straight away. Each member has a name and a value, you can loop over them with for status in Status, and you can look one up by value with Status("paid"). The first loop prints each member with !s (which gives Status.PAID) and its value with !r.

`match` compares a value against a series of patterns and runs the first case that fits. In next_step, case Status.PENDING: compares against a specific member, and `case _:` catches everything else, like an else.

In handle, the patterns describe the shape of the data. {"type": "order", "id": oid, ...} matches any dictionary with those keys (even if it has more) and captures the values into oid and total. The if total >= 100 part is a guard: if it fails, the next case is tried, and that is the one that accepts order 8.

("refund", oid, amount) matches any three-item sequence starting with "refund" (tuple or list; strings do not count). ["cancel", *ids] accepts any length and collects the rest into the list ids.

The last event, "ping", matches nothing and falls into case _. {event!r} prints it with repr(), quotes included.

Code · main.py

from enum import Enumclass Status(Enum):    PENDING = "pending"    PAID = "paid"    SHIPPED = "shipped"def next_step(status):    match status:        case Status.PENDING:            return "wait for payment"        case Status.PAID:            return "pack and ship"        case _:                       # anything else            return "nothing to do"def handle(event):    match event:                      # match by shape and capture values        case {"type": "order", "id": oid, "total": total} if total >= 100:            return f"order {oid}: big order, check stock"        case {"type": "order", "id": oid}:            return f"order {oid}: accepted"        case ("refund", oid, amount):            return f"refund {amount} EUR for order {oid}"        case ["cancel", *ids]:            return f"cancel {len(ids)} orders: {ids}"        case _:            return f"unknown event: {event!r}"for status in Status:    print(f"{status!s:<15} {status.value!r:<10} -> {next_step(status)}")for event in [{"type": "order", "id": 7, "total": 250}, {"type": "order", "id": 8},              ("refund", 7, 40), ["cancel", 3, 4], "ping"]:    print(handle(event))

Output · python3 main.py

Status.PENDING  'pending'  -> wait for payment
Status.PAID     'paid'     -> pack and ship
Status.SHIPPED  'shipped'  -> nothing to do
order 7: big order, check stock
order 8: accepted
refund 40 EUR for order 7
cancel 2 orders: [3, 4]
unknown event: 'ping'

Key points

  • Enum gives fixed names to a closed set of values.
  • match tries each case in order and runs the first one that fits.
  • Patterns can capture values into variables.
  • An if after the pattern adds a condition (a guard).
  • case _: catches whatever did not match.

Exercise

Add a CANCELLED state to Status and a case in next_step that returns "refund the customer"; then make refund events with a negative amount print a warning.