Python match Statement
The match statement compares a value against patterns and runs the first case that fits.
A Quick Reminder
The If...Else lesson showed a simple match. It needs Python 3.10 or newer. The shape is:
Example
match value:
case pattern1:
...
case pattern2:
...
case _:
...Python checks the cases from top to bottom. Only the first matching case runs. case _ is the "anything else" case.
match can do much more than compare numbers. It can look inside lists, dictionaries and objects. This is called structural pattern matching.
Several Values in One Case
Use | (read it as "or") to join values.
Example
def day_type(day):
match day.lower():
case "saturday" | "sunday":
return "Weekend"
case "friday":
return "Half day"
case "monday" | "tuesday" | "wednesday" | "thursday":
return "Working day"
case _:
return "Not a day"
for d in ["Sunday", "Friday", "Tuesday", "Funday"]:
print(d, "->", day_type(d))Sunday -> Weekend Friday -> Half day Tuesday -> Working day Funday -> Not a day
Guards: Adding a Condition
Add if after a pattern to check an extra condition. This is called a guard. A plain name in a pattern (like m below) catches the value so you can use it.
Example
def grade(m):
match m:
case m if m < 0 or m > 100:
return "Invalid"
case m if m >= 80:
return "A"
case m if m >= 60:
return "B"
case _:
return "C"
print(grade(91), grade(67), grade(40), grade(150))A B C Invalid
Matching Lists and Tuples
A pattern can describe the shape of a sequence. Names inside the pattern grab the matching items.
Example
def run(command):
match command.split():
case ["help"]:
print("Commands: add, remove, list")
case ["add", item]:
print(f"Adding {item}")
case ["add", item, qty]:
print(f"Adding {qty} x {item}")
case ["remove", *items]:
print("Removing", ", ".join(items))
case []:
print("Type something")
case _:
print("Unknown command")
run("help")
run("add milk")
run("add eggs 12")
run("remove bread butter jam")
run("")
run("dance")Commands: add, remove, list Adding milk Adding 12 x eggs Removing bread, butter, jam Type something Unknown command
*items collects the rest of the list, just like *args in a function. Writing this with if/elif would need many length checks and index lookups.
Matching Dictionaries
A dictionary pattern checks that certain keys exist and grabs their values. Extra keys are allowed.
Example
def handle(event):
match event:
case {"type": "order", "item": item, "qty": qty}:
print(f"New order: {qty} x {item}")
case {"type": "payment", "amount": int(amount)} if amount > 0:
print(f"Payment received: Rs {amount}")
case {"type": t}:
print(f"Ignoring event type '{t}'")
handle({"type": "order", "item": "biryani", "qty": 2, "table": 5})
handle({"type": "payment", "amount": 1800})
handle({"type": "payment", "amount": "free"})
handle({"type": "login"})New order: 2 x biryani Payment received: Rs 1800 Ignoring event type 'payment' Ignoring event type 'login'
int(amount) matches only when the value is an int. The text "free" is not, so that event falls to the last case. This is very handy for data that comes from JSON or an API.
Matching Objects
You can also match the type and attributes of an object. Dataclasses work very well with match.
Example
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def where(p):
match p:
case Point(x=0, y=0):
return "At the origin"
case Point(x=0, y=y):
return f"On the y-axis at {y}"
case Point(x=x, y=0):
return f"On the x-axis at {x}"
case Point():
return "Somewhere else"
case _:
return "Not a point"
print(where(Point(0, 0)))
print(where(Point(0, 7)))
print(where(Point(3, 0)))
print(where(Point(2, 5)))
print(where("hello"))At the origin On the y-axis at 7 On the x-axis at 3 Somewhere else Not a point
Pattern Cheat Sheet
| Pattern | Matches |
|---|---|
case 5: or case "yes": | That exact value |
| Values joined by the bar symbol, as in the weekend example | Any one of those values |
case x: | Anything, and stores it in x |
case _: | Anything (stores nothing) |
case [a, b]: | A sequence of exactly two items |
case [first, *rest]: | A sequence with at least one item |
case {"key": v}: | A dict that has that key |
case int(n): | An int, stored in n |
case Point(x=0): | A Point object whose x is 0 |
case p if p > 0: | Any value that also passes the guard |
A bare name like case total: does not compare with a variable called total. It always matches and overwrites the name. To compare with a stored constant, use a dotted name such as case Colour.RED:.
Exercise
Exercise
Which case matches the list ["add", "tea", "3"]?
