- What is match-case — Python's powerful pattern matching feature
- Why use it — cleaner than if-elif-else chains
- Basic syntax — how to write match-case statements
- Pattern types — literals, variables, sequences, and more
- Guards — adding extra conditions
- Real-world examples — practical use cases
What is Match-Case?
The match-case statement is a powerful feature introduced in Python 3.10. It's like a switch statement you might know from other languages, but much more powerful. It lets you match a value against multiple patterns and execute code based on which pattern matches.
Think of match-case like a sorting machine. You put something in, and it checks: "Is it an apple? Then put it in the fruit bin. Is it a carrot? Then put it in the vegetable bin." Each case is a different bin for a different kind of item.
Match-case is much more powerful than a simple switch statement because it can match not just values but also patterns in data structures.
💡 Key concept: Match-case lets you compare a value against multiple patterns and run the code for the first matching pattern.
Why Use Match-Case?
The Benefits of Match-Case
Match-case makes your code cleaner and more readable compared to long if-elif chains.
# Why Use Match-Case?
print("=" * 50)
print("WHY USE MATCH-CASE?")
print("=" * 50)
# ============================================================
# WITHOUT MATCH-CASE — Long If-Elif Chain
# ============================================================
print("\n1. WITHOUT MATCH-CASE")
def get_status_color_old(status):
if status == "success":
return "green"
elif status == "error":
return "red"
elif status == "warning":
return "yellow"
elif status == "info":
return "blue"
elif status == "pending":
return "orange"
else:
return "gray"
print(f" get_status_color_old('success'): {get_status_color_old('success')}")
print(f" get_status_color_old('error'): {get_status_color_old('error')}")
# Problems:
# 1. Lots of repetition
# 2. Easy to miss a case
# 3. Harder to read with many cases
# ============================================================
# WITH MATCH-CASE — Clean and Readable
# ============================================================
print("\n2. WITH MATCH-CASE")
def get_status_color_new(status):
match status:
case "success":
return "green"
case "error":
return "red"
case "warning":
return "yellow"
case "info":
return "blue"
case "pending":
return "orange"
case _:
return "gray"
print(f" get_status_color_new('success'): {get_status_color_new('success')}")
print(f" get_status_color_new('error'): {get_status_color_new('error')}")
print(" Benefits:")
print(" - No repetition of 'elif'")
print(" - Easier to read")
print(" - Cases are clearly separated")
print(" - _ is the default (like else)")
# ============================================================
# COMPARISON
# ============================================================
print("\n" + "-" * 30)
print("COMPARISON")
print("-" * 30)
print("""
- Cleaner than if-elif chains
- More readable
- Supports pattern matching (not just values)
- Can match on structure (tuples, lists, dicts)
- Default case with _
- Easier to maintain
""")
Benefits of match-case:
- Cleaner code — no repetitive elifs
- More readable — cases are clearly separated
- Pattern matching — match on structure, not just values
- Default case — use
_for catch-all - Easier to maintain — adding new cases is simple
Quick Check: What does the _ case do in match-case? (Answer: It acts as the default case, like else)
Basic Syntax
How to Write Match-Case
The syntax is simple: match value: followed by case pattern: blocks.
# Basic Match-Case Syntax
print("=" * 50)
print("BASIC MATCH-CASE SYNTAX")
print("=" * 50)
# ============================================================
# SIMPLE VALUE MATCHING
# ============================================================
print("\n1. SIMPLE VALUE MATCHING")
def get_day_name(day_number):
match day_number:
case 1:
return "Monday"
case 2:
return "Tuesday"
case 3:
return "Wednesday"
case 4:
return "Thursday"
case 5:
return "Friday"
case 6:
return "Saturday"
case 7:
return "Sunday"
case _:
return "Invalid day"
print(f" Day 1: {get_day_name(1)}")
print(f" Day 5: {get_day_name(5)}")
print(f" Day 10: {get_day_name(10)}")
# ============================================================
# MATCHING STRINGS
# ============================================================
print("\n2. MATCHING STRINGS")
def handle_command(command):
match command.lower():
case "start":
return "Starting the system..."
case "stop":
return "Stopping the system..."
case "restart":
return "Restarting the system..."
case "status":
return "System is running"
case "help":
return "Available commands: start, stop, restart, status, help"
case _:
return f"Unknown command: {command}"
print(f" start: {handle_command('start')}")
print(f" help: {handle_command('help')}")
print(f" unknown: {handle_command('unknown')}")
# ============================================================
# MATCHING MULTIPLE VALUES
# ============================================================
print("\n3. MATCHING MULTIPLE VALUES")
def get_response_code_type(code):
match code:
case 200 | 201 | 202:
return "Success"
case 301 | 302 | 307:
return "Redirect"
case 400 | 401 | 403 | 404:
return "Client Error"
case 500 | 501 | 502 | 503:
return "Server Error"
case _:
return "Unknown"
print(f" 200: {get_response_code_type(200)}")
print(f" 404: {get_response_code_type(404)}")
print(f" 500: {get_response_code_type(500)}")
print(f" 999: {get_response_code_type(999)}")
# ============================================================
# PATTERN VARIABLES
# ============================================================
print("\n4. PATTERN VARIABLES")
def describe_point(point):
match point:
case (0, 0):
return "Origin"
case (0, y):
return f"On Y-axis at y={y}"
case (x, 0):
return f"On X-axis at x={x}"
case (x, y):
return f"Point at ({x}, {y})"
case _:
return "Invalid point"
print(f" (0, 0): {describe_point((0, 0))}")
print(f" (0, 5): {describe_point((0, 5))}")
print(f" (3, 0): {describe_point((3, 0))}")
print(f" (2, 3): {describe_point((2, 3))}")
# ============================================================
# RULES TO REMEMBER
# ============================================================
print("\n" + "-" * 30)
print("RULES FOR MATCH-CASE")
print("-" * 30)
print("""
- match value: starts the matching
- case pattern: defines a pattern to match
- _ is the default (catch-all) case
- Patterns are checked in order
- First matching pattern is executed
- Use | for OR patterns (case 1 | 2:)
- Variables in patterns capture values
""")
Basic syntax key points:
- match value: — starts the matching
- case pattern: — defines a pattern to match
- _ — default/catch-all case
- | — OR pattern (case 1 | 2:)
- Variables — capture values from patterns
Quick Check: How do you match multiple values in one case? (Answer: Use | like case 1 | 2 | 3:)
Pattern Types
Different Kinds of Patterns
Match-case supports many different pattern types. Let's look at the most common ones.
# Pattern Types in Match-Case
print("=" * 50)
print("PATTERN TYPES")
print("=" * 50)
# ============================================================
# 1. LITERAL PATTERNS
# ============================================================
print("\n1. LITERAL PATTERNS")
def check_value(value):
match value:
case 0:
return "Zero"
case 1:
return "One"
case True:
return "True"
case False:
return "False"
case "hello":
return "Hello string"
case None:
return "None value"
case _:
return "Something else"
print(f" 0: {check_value(0)}")
print(f" 1: {check_value(1)}")
print(f" True: {check_value(True)}")
print(f" 'hello': {check_value('hello')}")
print(f" None: {check_value(None)}")
# ============================================================
# 2. VARIABLE PATTERNS (captures value)
# ============================================================
print("\n2. VARIABLE PATTERNS")
def describe_number(num):
match num:
case 0:
return "Zero"
case n if n < 0:
return f"Negative number: {n}"
case n if n > 0:
return f"Positive number: {n}"
print(f" 5: {describe_number(5)}")
print(f" -3: {describe_number(-3)}")
print(f" 0: {describe_number(0)}")
# ============================================================
# 3. SEQUENCE PATTERNS (lists, tuples)
# ============================================================
print("\n3. SEQUENCE PATTERNS")
def process_list(items):
match items:
case []:
return "Empty list"
case [x]:
return f"Single item: {x}"
case [x, y]:
return f"Two items: {x} and {y}"
case [x, y, *rest]:
return f"First: {x}, Second: {y}, Rest: {rest}"
case _:
return "Something else"
print(f" []: {process_list([])}")
print(f" [5]: {process_list([5])}")
print(f" [1, 2]: {process_list([1, 2])}")
print(f" [1, 2, 3, 4]: {process_list([1, 2, 3, 4])}")
# ============================================================
# 4. MAPPING PATTERNS (dictionaries)
# ============================================================
print("\n4. MAPPING PATTERNS")
def process_user(user):
match user:
case {"name": name, "age": age}:
return f"User {name} is {age} years old"
case {"name": name}:
return f"User {name} (age unknown)"
case {"age": age}:
return f"Age: {age} (name unknown)"
case _:
return "Invalid user data"
print(f" {{'name': 'Alice', 'age': 30}}: {process_user({'name': 'Alice', 'age': 30})}")
print(f" {{'name': 'Bob'}}: {process_user({'name': 'Bob'})}")
print(f" {{'age': 25}}: {process_user({'age': 25})}")
# ============================================================
# 5. CLASS PATTERNS
# ============================================================
print("\n5. CLASS PATTERNS")
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def process_point(p):
match p:
case Point(x=0, y=0):
return "Origin"
case Point(x=0, y=y):
return f"On Y-axis at y={y}"
case Point(x=x, y=0):
return f"On X-axis at x={x}"
case Point(x=x, y=y):
return f"Point ({x}, {y})"
case _:
return "Not a point"
p1 = Point(0, 0)
p2 = Point(0, 5)
p3 = Point(3, 4)
print(f" (0, 0): {process_point(p1)}")
print(f" (0, 5): {process_point(p2)}")
print(f" (3, 4): {process_point(p3)}")
# ============================================================
# 6. OR PATTERNS
# ============================================================
print("\n6. OR PATTERNS")
def classify_number(n):
match n:
case 0 | 1 | 2:
return "Small (0-2)"
case 3 | 4 | 5:
return "Medium (3-5)"
case 6 | 7 | 8 | 9:
return "Large (6-9)"
case _:
return "Other"
print(f" 1: {classify_number(1)}")
print(f" 4: {classify_number(4)}")
print(f" 8: {classify_number(8)}")
print(f" 10: {classify_number(10)}")
# ============================================================
# 7. WILDCARD PATTERN
# ============================================================
print("\n7. WILDCARD PATTERN (_)")
def wildcard_demo(value):
match value:
case 1:
return "One"
case 2:
return "Two"
case _:
return "Something else (wildcard)"
print(f" 1: {wildcard_demo(1)}")
print(f" 2: {wildcard_demo(2)}")
print(f" 3: {wildcard_demo(3)}")
Pattern types key points:
- Literal — exact values like 1, "hello", True
- Variable — captures the value (e.g.,
case n:) - Sequence — lists and tuples with
*rest - Mapping — dictionaries with keys
- Class — matches class instances
- OR — multiple patterns with
| - Wildcard —
_for anything
Quick Check: What pattern type would you use to match a dictionary with specific keys? (Answer: Mapping pattern like case {"name": name}:)
Using Guards
Adding Extra Conditions
Guards let you add extra conditions to your patterns using if after the pattern.
# Guards in Match-Case
print("=" * 50)
print("GUARDS IN MATCH-CASE")
print("=" * 50)
# ============================================================
# BASIC GUARDS
# ============================================================
print("\n1. BASIC GUARDS")
def categorize_age(age):
match age:
case a if a < 0:
return "Invalid age"
case a if a < 13:
return "Child"
case a if a < 18:
return "Teenager"
case a if a < 65:
return "Adult"
case a if a < 120:
return "Senior"
case _:
return "Invalid age"
print(f" -5: {categorize_age(-5)}")
print(f" 10: {categorize_age(10)}")
print(f" 16: {categorize_age(16)}")
print(f" 30: {categorize_age(30)}")
print(f" 70: {categorize_age(70)}")
print(f" 150: {categorize_age(150)}")
# ============================================================
# GUARDS WITH PATTERN VARIABLES
# ============================================================
print("\n2. GUARDS WITH PATTERN VARIABLES")
def evaluate_point(point):
match point:
case (x, y) if x == y:
return f"On diagonal: ({x}, {y})"
case (x, y) if x > y:
return f"Above diagonal: ({x}, {y})"
case (x, y) if x < y:
return f"Below diagonal: ({x}, {y})"
case _:
return "Not a point"
print(f" (3, 3): {evaluate_point((3, 3))}")
print(f" (5, 2): {evaluate_point((5, 2))}")
print(f" (2, 5): {evaluate_point((2, 5))}")
# ============================================================
# GUARDS WITH DICTIONARY PATTERNS
# ============================================================
print("\n3. GUARDS WITH DICTIONARY PATTERNS")
def validate_user(user):
match user:
case {"name": name, "age": age} if age >= 18:
return f"Adult user: {name} ({age})"
case {"name": name, "age": age} if age < 18:
return f"Minor user: {name} ({age})"
case {"name": name}:
return f"User: {name} (age unknown)"
case _:
return "Invalid user data"
print(f" {{'name': 'Alice', 'age': 25}}: {validate_user({'name': 'Alice', 'age': 25})}")
print(f" {{'name': 'Bob', 'age': 15}}: {validate_user({'name': 'Bob', 'age': 15})}")
print(f" {{'name': 'Charlie'}}: {validate_user({'name': 'Charlie'})}")
# ============================================================
# GUARDS WITH SEQUENCE PATTERNS
# ============================================================
print("\n4. GUARDS WITH SEQUENCE PATTERNS")
def analyze_numbers(numbers):
match numbers:
case [x, y, z] if x + y == z:
return f"{x} + {y} = {z}"
case [x, y, z] if x * y == z:
return f"{x} * {y} = {z}"
case [x, y, z] if x == y == z:
return f"All equal: {x}"
case [x, y, z]:
return f"Numbers: {x}, {y}, {z}"
case _:
return "Invalid sequence"
print(f" [2, 3, 5]: {analyze_numbers([2, 3, 5])}")
print(f" [2, 3, 6]: {analyze_numbers([2, 3, 6])}")
print(f" [3, 3, 3]: {analyze_numbers([3, 3, 3])}")
print(f" [1, 4, 7]: {analyze_numbers([1, 4, 7])}")
# ============================================================
# MULTIPLE GUARDS
# ============================================================
print("\n5. MULTIPLE GUARDS")
def classify_triangle(sides):
match sides:
case [a, b, c] if a <= 0 or b <= 0 or c <= 0:
return "Invalid triangle (negative sides)"
case [a, b, c] if a + b <= c or a + c <= b or b + c <= a:
return "Not a triangle"
case [a, b, c] if a == b == c:
return "Equilateral triangle"
case [a, b, c] if a == b or b == c or a == c:
return "Isosceles triangle"
case [a, b, c]:
return "Scalene triangle"
print(f" [3, 4, 5]: {classify_triangle([3, 4, 5])}")
print(f" [3, 3, 3]: {classify_triangle([3, 3, 3])}")
print(f" [3, 3, 5]: {classify_triangle([3, 3, 5])}")
print(f" [1, 1, 3]: {classify_triangle([1, 1, 3])}")
Guards key points:
- if condition — adds extra conditions to patterns
- Pattern variables — can be used in guards
- Complex logic — guards can use any boolean expression
- Order matters — more specific guards should come first
- Readability — guards keep conditions with their patterns
Quick Check: What is a guard in match-case? (Answer: An if condition after a pattern that adds an extra condition)
Complex Patterns
Advanced Pattern Matching
Match-case can handle complex nested patterns, making it very powerful for parsing data.
# Complex Patterns in Match-Case
print("=" * 50)
print("COMPLEX PATTERNS")
print("=" * 50)
# ============================================================
# NESTED PATTERNS
# ============================================================
print("\n1. NESTED PATTERNS")
def process_data(data):
match data:
case {"user": {"name": name, "age": age}, "status": status}:
return f"User {name} ({age}) has status: {status}"
case {"user": {"name": name}, "status": status}:
return f"User {name} (age unknown) has status: {status}"
case {"status": status}:
return f"Unknown user with status: {status}"
case _:
return "Invalid data format"
data1 = {"user": {"name": "Alice", "age": 30}, "status": "active"}
data2 = {"user": {"name": "Bob"}, "status": "inactive"}
data3 = {"status": "pending"}
print(f" Data1: {process_data(data1)}")
print(f" Data2: {process_data(data2)}")
print(f" Data3: {process_data(data3)}")
# ============================================================
# LIST WITH PATTERNS
# ============================================================
print("\n2. LIST WITH PATTERNS")
def parse_expression(expr):
match expr:
case ["add", a, b]:
return f"{a} + {b} = {a + b}"
case ["sub", a, b]:
return f"{a} - {b} = {a - b}"
case ["mul", a, b]:
return f"{a} * {b} = {a * b}"
case ["div", a, 0]:
return "Division by zero error"
case ["div", a, b]:
return f"{a} / {b} = {a / b}"
case ["pow", a, b]:
return f"{a} ^ {b} = {a ** b}"
case _:
return "Unknown operation"
print(f" ['add', 5, 3]: {parse_expression(['add', 5, 3])}")
print(f" ['mul', 4, 2]: {parse_expression(['mul', 4, 2])}")
print(f" ['div', 10, 0]: {parse_expression(['div', 10, 0])}")
print(f" ['pow', 2, 3]: {parse_expression(['pow', 2, 3])}")
# ============================================================
# MIXED PATTERNS
# ============================================================
print("\n3. MIXED PATTERNS")
def analyze_value(value):
match value:
case int() as n if n > 0:
return f"Positive integer: {n}"
case int() as n if n < 0:
return f"Negative integer: {n}"
case float() as f:
return f"Float: {f:.2f}"
case str() as s:
return f"String: {s}"
case list() as lst:
return f"List with {len(lst)} items"
case dict() as d:
return f"Dictionary with {len(d)} keys"
case _:
return "Unknown type"
print(f" 42: {analyze_value(42)}")
print(f" -5: {analyze_value(-5)}")
print(f" 3.14: {analyze_value(3.14)}")
print(f" 'hello': {analyze_value('hello')}")
print(f" [1, 2, 3]: {analyze_value([1, 2, 3])}")
print(f" {{'a': 1}}: {analyze_value({'a': 1})}")
# ============================================================
# AS PATTERNS (binding)
# ============================================================
print("\n4. AS PATTERNS (binding)")
def process_with_as(value):
match value:
case [1, 2, 3] as whole:
return f"Matched [1, 2, 3] as {whole}"
case {"name": name, "age": age} as person:
return f"Person: {person}"
case _:
return "No match"
print(f" [1, 2, 3]: {process_with_as([1, 2, 3])}")
print(f" {{'name': 'Alice', 'age': 30}}: {process_with_as({'name': 'Alice', 'age': 30})}")
# ============================================================
# NESTED MAPPINGS WITH VARIABLES
# ============================================================
print("\n5. NESTED MAPPINGS WITH VARIABLES")
def process_config(config):
match config:
case {"database": {"host": host, "port": port}}:
return f"Database: {host}:{port}"
case {"database": {"host": host}}:
return f"Database: {host} (default port)"
case {"cache": {"type": "redis", "host": host}}:
return f"Redis cache at {host}"
case {"cache": {"type": "memcached"}}:
return "Memcached cache"
case _:
return "Unknown config"
config1 = {"database": {"host": "localhost", "port": 5432}}
config2 = {"database": {"host": "server.com"}}
config3 = {"cache": {"type": "redis", "host": "cache.local"}}
config4 = {"cache": {"type": "memcached"}}
print(f" Config1: {process_config(config1)}")
print(f" Config2: {process_config(config2)}")
print(f" Config3: {process_config(config3)}")
print(f" Config4: {process_config(config4)}")
Complex patterns key points:
- Nested patterns — match nested structures
- Type checking — use
int() as nto match types - as pattern — bind the whole matched object
- Mixed patterns — combine different pattern types
- Flexible — handle complex data structures
Quick Check: What does as do in a pattern? (Answer: It binds the matched object to a variable)
Real-World Example
Building a JSON Parser
# Real-World Example: JSON Data Processor
import json
from datetime import datetime
print("=" * 60)
print("JSON DATA PROCESSOR")
print("=" * 60)
# ============================================================
# SAMPLE DATA
# ============================================================
sample_data = {
"type": "user_event",
"user": {
"id": 123,
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 30,
"preferences": {
"theme": "dark",
"notifications": True
}
},
"event": {
"type": "login",
"timestamp": "2024-01-15T10:30:00",
"details": {
"ip": "192.168.1.1",
"device": "Chrome"
}
},
"metadata": {
"source": "web",
"version": "2.0"
}
}
print("\n1. PROCESSING USER DATA")
def process_user_data(data):
match data:
case {
"user": {
"name": name,
"email": email,
"age": age,
"preferences": {"theme": theme, "notifications": notifications}
},
"metadata": {"source": source}
} if age >= 18:
return {
"name": name,
"email": email,
"age": age,
"theme": theme,
"notifications": notifications,
"source": source,
"status": "valid_adult"
}
case {
"user": {
"name": name,
"email": email,
"age": age,
"preferences": preferences
}
} if age < 18:
return {
"name": name,
"email": email,
"age": age,
"status": "minor",
"message": "Parental consent required"
}
case {"user": user_data}:
return {"status": "user_found", "data": user_data}
case _:
return {"status": "invalid", "message": "Invalid user data"}
result = process_user_data(sample_data)
print(" Processed user data:")
for key, value in result.items():
print(f" {key}: {value}")
print("\n2. PROCESSING EVENTS")
def process_event(data):
match data:
case {
"event": {
"type": "login",
"timestamp": timestamp,
"details": {"ip": ip, "device": device}
}
}:
return {
"event_type": "login",
"timestamp": timestamp,
"ip": ip,
"device": device,
"status": "login_event"
}
case {
"event": {
"type": "purchase",
"details": {"item": item, "price": price}
}
}:
return {
"event_type": "purchase",
"item": item,
"price": price,
"status": "purchase_event"
}
case {"event": event_data}:
return {"event_type": "unknown", "data": event_data}
case _:
return {"status": "invalid", "message": "No event data"}
event_result = process_event(sample_data)
print(" Processed event:")
for key, value in event_result.items():
print(f" {key}: {value}")
print("\n3. PROCESSING RESPONSES")
def process_response(response):
match response:
case {"status": "success", "data": data}:
return {"status": "OK", "data": data}
case {"status": "error", "code": 400, "message": msg}:
return {"status": "ERROR", "code": 400, "message": f"Bad Request: {msg}"}
case {"status": "error", "code": 404}:
return {"status": "ERROR", "code": 404, "message": "Not Found"}
case {"status": "error", "code": code}:
return {"status": "ERROR", "code": code, "message": "Server Error"}
case _:
return {"status": "UNKNOWN", "message": "Invalid response"}
responses = [
{"status": "success", "data": {"id": 1, "name": "Item"}},
{"status": "error", "code": 400, "message": "Invalid input"},
{"status": "error", "code": 404},
{"status": "error", "code": 500}
]
print(" Processing responses:")
for i, resp in enumerate(responses, 1):
result = process_response(resp)
print(f" Response {i}: {result}")
print("\n" + "=" * 60)
print("KEY TAKEAWAYS:")
print("=" * 60)
print("""
- Match-case is perfect for processing JSON data
- Handles nested structures easily
- Guards add validation conditions
- Clean and readable code
- Less error-prone than manual checks
""")
Real-world example key points:
- User data — match nested user profiles
- Events — handle different event types
- Responses — process API responses
- Validation — guards check conditions
- Clean code — no complex if-else chains
Quick Check: What's a good use case for match-case? (Answer: Processing JSON data, API responses, and complex data structures)
Best Practices
Using Match-Case Effectively
# Best Practices for Match-Case
print("=" * 60)
print("BEST PRACTICES FOR MATCH-CASE")
print("=" * 60)
# ============================================================
# 1. USE FOR EXHAUSTIVE PATTERN MATCHING
# ============================================================
print("\n1. USE FOR EXHAUSTIVE PATTERN MATCHING")
# Good - covers all cases
def process_result(result):
match result:
case {"status": "ok", "data": data}:
return data
case {"status": "error", "message": msg}:
return f"Error: {msg}"
case _:
return "Unknown result"
# Bad - missing case (no default)
# def process_result_bad(result):
# match result:
# case {"status": "ok", "data": data}:
# return data
print(" Always include a default case")
# ============================================================
# 2. ORDER PATTERNS FROM SPECIFIC TO GENERAL
# ============================================================
print("\n2. ORDER PATTERNS FROM SPECIFIC TO GENERAL")
def process_value(value):
match value:
case 0: # Most specific first
return "Zero"
case int(): # More general
return f"Integer: {value}"
case str(): # Even more general
return f"String: {value}"
case _: # Most general last
return "Unknown"
print(" Specific patterns first, general last")
# ============================================================
# 3. USE GUARDS FOR EXTRA CONDITIONS
# ============================================================
print("\n3. USE GUARDS FOR EXTRA CONDITIONS")
def classify_number(n):
match n:
case x if x == 0:
return "Zero"
case x if x > 0:
return "Positive"
case x if x < 0:
return "Negative"
print(" Guards add extra conditions to patterns")
# ============================================================
# 4. KEEP IT READABLE
# ============================================================
print("\n4. KEEP IT READABLE")
# Good - clear and readable
def process_user_data_good(data):
match data:
case {"name": name, "age": age}:
return f"{name} is {age} years old"
case {"name": name}:
return f"{name} (age unknown)"
case _:
return "Invalid user"
# Bad - too complex and hard to read
# def process_user_data_bad(data):
# match data:
# case {"name": name, "age": age, "city": city, "country": country, "phone": phone} if age > 18 and city == "NYC":
# return f"{name} ({phone})"
# case _:
# return "Invalid"
print(" Keep patterns simple and readable")
# ============================================================
# 5. USE TYPE CHECKING PATTERNS
# ============================================================
print("\n5. USE TYPE CHECKING PATTERNS")
def process_value_type(value):
match value:
case int() as n:
return f"Integer: {n}"
case float() as f:
return f"Float: {f}"
case str() as s:
return f"String: {s}"
case list() as l:
return f"List: {l}"
case _:
return "Unknown type"
print(" Type checking patterns make code cleaner")
# ============================================================
# 6. SUMMARY
# ============================================================
print("\n" + "=" * 60)
print("BEST PRACTICES SUMMARY")
print("=" * 60)
print("""
- Always include a default case
- Order from specific to general
- Use guards for extra conditions
- Keep patterns readable
- Use type checking patterns
- Don't overcomplicate
- Match-case is for readability
""")
Best practices summary:
- Always include default — use
_case - Order matters — specific patterns first
- Use guards — for extra conditions
- Keep it readable — don't overcomplicate
- Type checking — use
int() as npatterns - Don't overuse — match-case for readability
Quick Check: Should you always include a default case? (Answer: Yes, use _ to handle unexpected values)
Try It Yourself
Experiment with match-case in the editor below.
MATCH-CASE - PRACTICE
==================================================
1. BASIC MATCH-CASE
'active': User is active
'unknown': Unknown status
2. MATCHING NUMBERS WITH GUARDS
95: A
75: C
50: F
-5: Invalid score
3. MATCHING TUPLES
(0, 0): Origin
(0, 5): Y-axis at 5
(3, 0): X-axis at 3
(2, 3): Point (2, 3)
4. MATCHING DICTIONARIES
{'name': 'Alice', 'age': 30}: Alice is 30 years old
{'name': 'Bob'}: Bob (age unknown)
{}: Invalid person data
You've Got It!
You now understand match-case in Python. You know how to use pattern matching, guards, and complex patterns for cleaner code.
Quick Quiz
Test what you've learned:
_ case do in match-case?Frequently Asked Questions
What is match-case in Python?
How is match-case different from if-elif?
Can I use match-case in older Python versions?
What patterns can I match?
Is match-case faster than if-elif?
Can I use match-case with walrus operator?
case x if (y := x + 1) > 5: This can make your patterns even more powerful.
Where to Go From Here
Now that you understand match-case in Python, check out these related topics:
Walrus Operator
Learn about the walrus operator (:=) for assignment expressions.
Learn More →Decorators
Learn about decorators — another advanced Python feature.
Learn More →Type Hints
Learn about type hints and how they work with pattern matching.
Learn More →