🎭 The Magic Sorting Hat: Python’s Match Statement
Imagine you’re a mail sorter at the world’s busiest post office. Every package that arrives needs to go to a different destination. You could check each one with tons of if-else statements… OR you could use Python’s magical sorting hat—the match statement!
🌟 What is Pattern Matching?
Think of pattern matching like a smart cookie cutter. You have a piece of dough (your data), and you’re checking which cookie cutter shape it fits perfectly.
Real Life Example:
- A vending machine checks what button you pressed
- If you press A1 → gives you chips
- If you press B2 → gives you candy
- If you press something unknown → shows “Invalid”
Python’s match does exactly this—but for your code!
🎯 Match Statement Basics
The match statement looks at a value and compares it against different patterns. When it finds a match, it runs that code block.
The Simple Recipe
match item:
case "apple":
print("It's a fruit!")
case "carrot":
print("It's a veggie!")
case _:
print("I don't know this!")
🍕 Pizza Order Example
Imagine you work at a pizza shop. Customers say what size they want:
def get_price(size):
match size:
case "small":
return 8
case "medium":
return 12
case "large":
return 16
case _:
return "Unknown size!"
print(get_price("medium")) # 12
print(get_price("giant")) # Unknown size!
What’s happening?
- Python looks at
size - Checks: Is it “small”? No → move on
- Checks: Is it “medium”? YES! → return 12
- Done! (Never checks “large”)
🔑 Key Points
| Part | What It Does |
|---|---|
match value: |
The thing you’re checking |
case pattern: |
What you’re looking for |
| Indented code | What happens when matched |
🏗️ Structural Patterns
This is where match gets super powerful. You can match the shape of data, not just exact values!
📦 Matching Lists (Sequences)
Imagine sorting toy boxes by what’s inside:
def describe_box(items):
match items:
case []:
return "Empty box"
case [one]:
return f"Box with 1 item: {one}"
case [first, second]:
return f"Two items: {first}, {second}"
case [first, *rest]:
return f"Starts with {first}, plus {len(rest)} more"
print(describe_box([])) # Empty box
print(describe_box(["toy"])) # Box with 1 item: toy
print(describe_box(["a", "b"])) # Two items: a, b
print(describe_box([1, 2, 3, 4])) # Starts with 1, plus 3 more
The Magic *rest:
- The star
*captures “everything else” - Like saying “I want the first cookie, and put the rest in a bag”
🏠 Matching Dictionaries
Check what keys exist in your data:
def check_user(data):
match data:
case {"name": n, "age": a}:
return f"{n} is {a} years old"
case {"name": n}:
return f"Found {n}, age unknown"
case {}:
return "Empty data"
user1 = {"name": "Ali", "age": 10}
user2 = {"name": "Sara"}
print(check_user(user1)) # Ali is 10 years old
print(check_user(user2)) # Found Sara, age unknown
Notice: The variable names n and a capture the actual values!
🎁 Pattern Matching Flow
graph TD A["Data Arrives"] --> B{Match Against Patterns} B --> C["Pattern 1: Empty?"] C -->|Yes| D["Handle Empty"] C -->|No| E["Pattern 2: One Item?"] E -->|Yes| F["Handle Single"] E -->|No| G["Pattern 3: Two Items?"] G -->|Yes| H["Handle Pair"] G -->|No| I["Wildcard Pattern"] I --> J["Handle Everything Else"]
🛡️ Guards in Match Statements
Sometimes matching the shape isn’t enough. You also need to check a condition. That’s what guards do!
🚦 Adding Extra Checks with if
def rate_score(score):
match score:
case n if n >= 90:
return "⭐ Excellent!"
case n if n >= 70:
return "👍 Good job!"
case n if n >= 50:
return "📚 Keep trying"
case _:
return "💪 Need more practice"
print(rate_score(95)) # ⭐ Excellent!
print(rate_score(75)) # 👍 Good job!
print(rate_score(45)) # 💪 Need more practice
How guards work:
case ncaptures the value into variablenif n >= 90is the guard—extra condition- Both must be true to match!
🎮 Game Level Example
def player_status(player):
match player:
case {"health": h, "shield": s} if h > 50 and s > 0:
return "Strong & Protected 🛡️"
case {"health": h} if h > 50:
return "Strong but Exposed ⚔️"
case {"health": h} if h > 0:
return "Wounded! Find health pack 🏥"
case _:
return "Game Over 💀"
p1 = {"health": 80, "shield": 20}
p2 = {"health": 60}
p3 = {"health": 15}
print(player_status(p1)) # Strong & Protected 🛡️
print(player_status(p2)) # Strong but Exposed ⚔️
print(player_status(p3)) # Wounded! Find health pack 🏥
🧠 Guard Decision Flow
graph TD A["Check Pattern"] --> B{Shape Matches?} B -->|No| C["Try Next Pattern"] B -->|Yes| D{Guard Condition True?} D -->|No| C D -->|Yes| E["Execute This Case!"]
🃏 Wildcard Patterns
The wildcard _ is your catch-all safety net. It matches absolutely anything!
🎪 The Ultimate Fallback
def identify(thing):
match thing:
case 0:
return "Zero"
case 1:
return "One"
case _:
return "Something else entirely!"
print(identify(0)) # Zero
print(identify(1)) # One
print(identify(999)) # Something else entirely!
print(identify("hello")) # Something else entirely!
🚨 Important: Order Matters!
The wildcard catches EVERYTHING. So always put it last:
# ❌ WRONG - wildcard first catches everything!
match color:
case _: # This catches ALL!
print("Unknown")
case "red": # Never reached!
print("Red")
# ✅ CORRECT - wildcard last
match color:
case "red":
print("Red")
case "blue":
print("Blue")
case _: # Only unmatched colors reach here
print("Unknown color")
🎯 Using _ Inside Patterns
The wildcard can ignore parts you don’t care about:
def get_first(items):
match items:
case [first, _, _]:
return f"First of three: {first}"
case [first, *_]:
return f"First item: {first}"
case _:
return "No items!"
print(get_first([10, 20, 30])) # First of three: 10
print(get_first([5, 6, 7, 8, 9])) # First item: 5
print(get_first([])) # No items!
The *_ trick: Capture-and-ignore any number of remaining items!
🎨 Putting It All Together
Let’s combine everything into one powerful example:
def process_command(cmd):
match cmd:
case ["move", direction] if direction in ["up", "down", "left", "right"]:
return f"Moving {direction}! 🏃"
case ["attack", target, power] if int(power) > 50:
return f"SUPER attack on {target}! 💥"
case ["attack", target, _]:
return f"Normal attack on {target}! ⚔️"
case ["heal"]:
return "Healing! 💚"
case [action, *_]:
return f"Unknown action: {action} 🤔"
case _:
return "Empty command! 🚫"
print(process_command(["move", "up"])) # Moving up! 🏃
print(process_command(["attack", "dragon", 80])) # SUPER attack on dragon! 💥
print(process_command(["attack", "goblin", 30])) # Normal attack on goblin! ⚔️
print(process_command(["dance", "salsa"])) # Unknown action: dance 🤔
🏆 Quick Summary
| Pattern Type | Example | What It Does |
|---|---|---|
| Literal | case 42: |
Matches exact value |
| Capture | case x: |
Captures into variable |
| Sequence | case [a, b]: |
Matches list structure |
| Star | case [first, *rest]: |
Captures remaining items |
| Mapping | case {"key": val}: |
Matches dict keys |
| Guard | case x if x > 0: |
Adds condition |
| Wildcard | case _: |
Matches anything |
🌈 Why You’ll Love Match Statements
- Cleaner than if-else chains — No more nested confusion!
- Captures values automatically — Extract data while matching
- Handles complex structures — Lists, dicts, nested data
- Guards add power — Combine structure + conditions
- Wildcard for safety — Never miss a case
Think of match as your code’s smart assistant. It looks at your data, figures out what shape it has, checks your conditions, and picks the perfect response. No more messy if-else chains—just clean, readable, powerful pattern matching!
You’re ready to be a pattern matching wizard! 🧙♂️✨
