match Statements
When one value can be one of many things, a long if/elif chain gets repetitive. Python's match statement, added in Python 3.10, compares a value against a list of patterns and runs the first one that fits. It can do what a "switch" does in other languages, and much more: it can also look inside lists and dictionaries and pull values out of them.
In this lesson you will match simple values, combine options, capture parts of lists and dictionaries, check types with class patterns, add extra conditions with guards, and avoid the one mistake that catches almost everyone.
Matching literal values
The simplest patterns are literals: fixed values such as numbers and strings. Python checks each case from top to bottom and runs the first one that matches.
coach = "3A"
match coach:
case "1A":
print("First AC")
case "2A":
print("AC 2-tier")
case "3A" | "3E":
print("AC 3-tier")
case "SL":
print("Sleeper")
case _:
print("Unknown coach code")AC 3-tier
Two pattern features appear here:
|means "or":"3A" | "3E"matches either code._is the wildcard. It matches anything, socase _:works likeelse. Put it last.
Only one case runs, and there is no "fall through" to the next case as in C or JavaScript, so you never need break. If no case matches and there is no case _:, nothing happens.
Sequence patterns
A pattern written like a list matches a sequence (a list or tuple) with the same shape, and a plain name inside it captures the value at that position. *rest captures any number of remaining items.
The for loop below runs the match once for each command; you will learn loops in for Loops.
commands = ["go north", "pick up lamp", "drop key coin", "quit", "dance"]
for text in commands:
match text.split():
case ["go", direction]:
print("Moving", direction)
case ["pick", "up", item]:
print("Picked up", item)
case ["drop", *items]:
print("Dropped", ", ".join(items))
case ["quit" | "exit"]:
print("Bye!")
case _:
print("I don't understand:", text)Moving north Picked up lamp Dropped key, coin Bye! I don't understand: dance
["go", direction] matches a two-item list whose first item is "go", and stores the second item in direction. Imagine writing the same thing with if, len() and indexes: it would take three times as many lines. Note that strings are not treated as sequences here, so case [a, b]: never matches the string "ab".
Mapping patterns
A pattern written like a dictionary matches a mapping (such as a dict) that has those keys. Extra keys in the data are ignored, which makes this perfect for data from forms and web APIs.
payment = {"method": "upi", "amount": 499, "upi_id": "priya@okbank"}
match payment:
case {"method": "upi", "upi_id": upi_id, "amount": amount}:
print(f"UPI payment of ₹{amount} from {upi_id}")
case {"method": "card", "amount": amount}:
print(f"Card payment of ₹{amount}")
case {"method": "cash"}:
print("Cash payment")
case _:
print("Invalid payment")UPI payment of ₹499 from priya@okbank
The first case needs "method" to be exactly "upi" and needs the keys "upi_id" and "amount" to exist; their values are captured. Change "method" to "cash" and run it again. You will learn about dictionaries in Dictionaries.
Guards: an extra condition
Add if condition after a pattern to make a guard. The case runs only if the pattern matches and the guard is true. If the guard is false, Python moves on to the next case.
order = {"item": "samosa", "qty": 40}
match order:
case {"item": item, "qty": qty} if qty >= 25:
print(f"Bulk order: {qty} x {item}, call the kitchen")
case {"item": item, "qty": qty} if qty > 0:
print(f"Normal order: {qty} x {item}")
case _:
print("Empty or invalid order")Bulk order: 40 x samosa, call the kitchen
Class patterns
A class pattern looks like a call: int(), str(), float(). It matches values of that type. With a name inside the brackets, str(text), it also captures the value. The word as captures whatever matched a larger pattern.
value = 3.5
match value:
case bool():
print("A Boolean:", value)
case int() | float() as number:
print("A number, doubled:", number * 2)
case str(text):
print("Some text:", text.upper())
case None:
print("No value")
case _:
print("Something else")A number, doubled: 7.0
bool() comes first on purpose: bool is a subclass of int, so True would otherwise match int(). Class patterns also work with your own classes, where you match attributes by name. This example uses a dataclass, which you will meet in Dataclasses & Enums:
from dataclasses import dataclass
@dataclass
class Order:
item: str
qty: int
order = Order("tea", 2)
match order:
case Order(item="tea", qty=qty):
print(f"{qty} cups of chai")
case Order(item=item, qty=qty):
print(f"{qty} x {item}")2 cups of chai
Common mistakes
- Using a bare name as a constant. If you write
case RED:whereRED = "red"is a variable, Python does not compare with it. A bare name is a capture pattern: it matches anything and stores the value inRED. Python even refuses to run such code when more cases follow, withSyntaxError: name capture 'RED' makes remaining patterns unreachable. Use a literal (case "red":) or a dotted name such asColour.REDfrom an enum. - Putting
case _:first. The wildcard matches everything, so later cases would never run. - Using
matchfor simple true or false checks.if/elseis clearer for one condition;matchshines when there are many shapes or values.
match and case are soft keywords: they act as keywords only in this statement, so old code with a variable named match still works.
Key points
match value:tries eachcasefrom top to bottom and runs only the first one that matches.- Literal patterns match fixed values;
|combines options;_matches anything. - Sequence patterns like
["go", direction]and[first, *rest]check a list's shape and capture items. - Mapping patterns like
{"method": "upi", "amount": amount}check keys and ignore extra keys. - Class patterns like
int(),str(text)andOrder(item=item)check types and attributes. - A guard
case pattern if condition:adds an extra test; bare names capture, they never compare.
Exercise
Write a match statement for a food delivery app. The variable event is a tuple. Handle these shapes: ("placed", order_id) prints "Order 101 placed", ("delivered", order_id, minutes) prints a thank-you message when minutes is 30 or less and an apology otherwise, and ("cancelled", order_id, reason) prints the reason. Anything else prints "Unknown event". Test it with event = ("delivered", 101, 42).
Show solution
event = ("delivered", 101, 42)
match event:
case ("placed", order_id):
print(f"Order {order_id} placed")
case ("delivered", order_id, minutes) if minutes <= 30:
print(f"Order {order_id} delivered in {minutes} minutes. Thank you!")
case ("delivered", order_id, minutes):
print(f"Sorry, order {order_id} took {minutes} minutes")
case ("cancelled", order_id, reason):
print(f"Order {order_id} cancelled: {reason}")
case _:
print("Unknown event")Sorry, order 101 took 42 minutes
Sequence patterns work with tuples as well as lists. The guard sends fast deliveries to the first "delivered" case, and everything else falls through to the second one.
Quick Quiz
1. What does case _: do in a match statement?
_ is the wildcard pattern; it always matches, so it belongs at the end.
2. Which pattern matches the list ["drop", "key", "coin"] and captures the last two items?
*items captures any number of remaining items into a list.
3. With RED = "red", what does case RED: do?
A bare name is a capture pattern. Use a literal or a dotted name to compare with a constant.
4. When does a case with a guard, case [x, y] if x > y:, run?
The pattern must match first; then the guard must also be true.
Learn Python with a mentor
Free lessons get you started. Our Python Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
