match-case is Python’s structural pattern matching feature (introduced in Python 3.10).
Think of it as a much more powerful switch, designed to match not just values, but also shapes and structures of data.
Many people think that match-case is a fancy way for doing if statements. Well... you can, but its purpose goes beyond that.
1. Basic idea (value matching)
Yes, you can use it as a replacement for if statements. At its simplest, it looks like a switch statement in other languages:
def http_status_message(status):
match status:
case 200:
return "OK"
case 404:
return "Not Found"
case 500:
return "Server Error"
case _:
return "Unknown status"
Key points:
-
match <expression>: evaluates once - Each case is tried top to bottom
-
_is the wildcard (default case)
This is equivalent to using an if statement:
def http_status_message(status):
if status == 200:
return "OK"
elif status == 404:
return "Not Found"
elif status == 500:
return "Server Error"
else:
return "Unknown status"
But you don't gain too much here by using match.
I can even say that here you don't need an if statment either. Just use a dictionary when is data disguised as logic.
def http_status_message(status):
messages = {
200: "OK",
404: "Not Found",
500: "Server Error",
}
return messages.get(status, "Unknown status")
2. Structural pattern-matching (shapes and structures of data mataching)
It can destructure objects, check types, and bind variables.
Tuple / sequence matching
point = (0, 5)
match point:
case (0, y):
print(f"On Y axis at y={y}")
case (x, 0):
print(f"On X axis at x={x}")
case (x, y):
print(f"At ({x}, {y})")
What’s happening:
(0, y) means:
- first element must be 0
- second element is captured as
y
This is not equality testing, you're not checking the value, you're checking the shape of the object.
3. Matching lists with rest (*)
numbers = [1, 2, 3, 4]
match numbers:
case [first, *rest]:
print(first, rest)
Rules:
-
*restcaptures “the remaining elements” - Works only once per pattern
4. Type and class matching
You can match by type, and even unpack attributes.
Simple type matching.
def handle(value):
match value:
case int():
print("Integer")
case str():
print("String")
case list():
print("List")
Class pattern matching
class User:
def __init__(self, name, is_admin):
self.name = name
self.is_admin = is_admin
user = User("Alice", True)
match user:
case User(name=name, is_admin=True):
print(f"Admin: {name}")
case User(name=name):
print(f"User: {name}")
This checks:
- instance of User
- then matches attributes
5. Guards (if inside case)
Sometimes structure isn’t enough, sometimes you need conditions.
match value:
case int(x) if x > 0:
print("Positive integer")
case int(x):
print("Non-positive integer")
6. OR patterns (|)
match command:
case "start" | "run" | "go":
print("Starting…")
case "stop" | "quit":
print("Stopping…")
7. Dict (mapping) matching
data = {"type": "error", "code": 404}
match data:
case {"type": "error", "code": code}:
print(f"Error code {code}")
Notes:
- Extra keys are allowed by default
- Only listed keys must match
This is what you take after reading this blog post:
-
match-caseasks: “Does this value have this shape?” -
ifasks: “Is this condition true?”
Extra reading:
https://www.pythonmorsels.com/switch-case-in-python
Top comments (0)