Python 3.10 added a new way to branch on a value: the match statement. It looks like the switch statement from other languages, but it does more — it can match patterns, not just exact values.
The simple form
status: str = "approved"
match status:
case "approved":
print("Welcome aboard.")
case "pending":
print("Please wait.")
case "rejected":
print("Sorry, try again next time.")
case _:
print("Unknown status.")
match status:says “look at the value ofstatus”.- Each
casechecks one option. case _:is the catch-all —_matches anything, like anelse.
This is equivalent to a chain of if/elif/else, just easier to read when you’re branching on the same value.
Matching multiple values in one case
Use the | operator to match any of several values:
match status:
case "approved" | "verified":
print("Welcome.")
case "pending" | "in_review":
print("Hold on.")
case _:
print("Other.")
Matching with conditions (guards)
You can add an if to a case — called a guard. The case only matches if both the pattern fits and the guard is true:
age: int = 17
match age:
case n if n < 13:
print("Child")
case n if n < 18:
print("Teenager")
case n if n < 65:
print("Adult")
case _:
print("Senior")
The variable n captures the value of age so the guard can reference it.
Matching shapes — the real power
match can pull apart lists, tuples, and dictionaries. This is where it goes beyond a simple switch.
Matching a tuple
def describe_point(point: tuple[int, int]) -> str:
match point:
case (0, 0):
return "Origin"
case (0, _):
return "On the Y-axis"
case (_, 0):
return "On the X-axis"
case (x, y):
return f"At ({x}, {y})"
print(describe_point((0, 0))) # Origin
print(describe_point((0, 5))) # On the Y-axis
print(describe_point((3, 4))) # At (3, 4)
(0, 0)matches the exact tuple(0, 0).(0, _)matches any tuple starting with 0.(x, y)matches any 2-tuple, and bindsxandyto its values.
We won’t go through every advanced pattern (lists, dicts, classes) here — match is rarely used at the beginner level. But you’ll see it in modern Python code and it’s worth recognising.
Matching a dictionary
event: dict[str, str | int] = {"type": "click", "x": 10, "y": 20}
match event:
case {"type": "click", "x": x, "y": y}:
print(f"Click at ({x}, {y})")
case {"type": "keypress", "key": key}:
print(f"Key pressed: {key}")
case _:
print("Unknown event")
The pattern {"type": "click", "x": x, "y": y} matches dictionaries with a type key equal to "click", and binds the x and y keys’ values.
When to use match vs if/elif/else
- One or two branches — use
if/else. - Several branches checking the same variable for exact values —
matchreads better than a longelifchain. - Branching on the shape of data (tuples, dicts) —
matchis the right tool.
You won’t reach for match every day. But when you need it, it’s much cleaner than the alternatives.
Summary of Section 4
You now know how to:
- Make decisions with
if/elif/else - Loop through collections with
for - Loop while a condition holds with
while - Skip or exit loops with
continueandbreak - Generate numbers with
range() - Branch on patterns with
match/case
With control flow, variables, types, and operators, you can write small but real programs. Next up is the biggest building block of all — functions.
What’s next
Section 5: Functions — defining your own reusable blocks of code, with type hints.