mirror of
https://github.com/blshaer/python-by-example.git
synced 2026-03-27 23:29:25 +01:00
244 lines
8.3 KiB
Python
244 lines
8.3 KiB
Python
"""
|
|
================================================================================
|
|
File: 03_match_case.py
|
|
Topic: Pattern Matching with match-case (Python 3.10+)
|
|
================================================================================
|
|
|
|
This file demonstrates Python's structural pattern matching introduced in
|
|
Python 3.10. It's similar to switch/case in other languages but much more
|
|
powerful with pattern matching capabilities.
|
|
|
|
Key Concepts:
|
|
- Basic match-case syntax
|
|
- The underscore (_) as wildcard/default case
|
|
- Pattern matching with guards
|
|
- Matching sequences and mappings
|
|
- Capturing values in patterns
|
|
|
|
Note: This requires Python 3.10 or later!
|
|
|
|
================================================================================
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 1. Basic match-case (Similar to switch/case)
|
|
# -----------------------------------------------------------------------------
|
|
# The simplest form of pattern matching
|
|
|
|
def get_day_name(day_number: int) -> str:
|
|
"""Convert day number to day name."""
|
|
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 _: # Default case (underscore is wildcard)
|
|
return "Invalid day"
|
|
|
|
print("--- Basic match-case ---")
|
|
print(f"Day 1: {get_day_name(1)}")
|
|
print(f"Day 5: {get_day_name(5)}")
|
|
print(f"Day 9: {get_day_name(9)}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 2. Multiple Patterns in One Case
|
|
# -----------------------------------------------------------------------------
|
|
# Use the | operator to match multiple values
|
|
|
|
def categorize_day(day: str) -> str:
|
|
"""Categorize day as weekday or weekend."""
|
|
match day.lower():
|
|
case "saturday" | "sunday":
|
|
return "Weekend - Time to relax!"
|
|
case "monday" | "tuesday" | "wednesday" | "thursday" | "friday":
|
|
return "Weekday - Time to work!"
|
|
case _:
|
|
return "Unknown day"
|
|
|
|
print("\n--- Multiple Patterns ---")
|
|
print(f"Saturday: {categorize_day('Saturday')}")
|
|
print(f"Monday: {categorize_day('Monday')}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 3. Pattern Matching with Guards
|
|
# -----------------------------------------------------------------------------
|
|
# Add conditions using 'if' after the pattern
|
|
|
|
def evaluate_score(score: int) -> str:
|
|
"""Evaluate score with guards."""
|
|
match score:
|
|
case n if n < 0:
|
|
return "Invalid score (negative)"
|
|
case n if n > 100:
|
|
return "Invalid score (over 100)"
|
|
case n if n >= 90:
|
|
return f"Grade A - Excellent! ({n}%)"
|
|
case n if n >= 80:
|
|
return f"Grade B - Good! ({n}%)"
|
|
case n if n >= 70:
|
|
return f"Grade C - Satisfactory ({n}%)"
|
|
case n if n >= 60:
|
|
return f"Grade D - Pass ({n}%)"
|
|
case _:
|
|
return f"Grade F - Fail ({score}%)"
|
|
|
|
print("\n--- Pattern with Guards ---")
|
|
print(evaluate_score(95))
|
|
print(evaluate_score(75))
|
|
print(evaluate_score(45))
|
|
print(evaluate_score(-10))
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 4. Matching Sequences (Tuples/Lists)
|
|
# -----------------------------------------------------------------------------
|
|
# Match against list or tuple patterns
|
|
|
|
def describe_point(point):
|
|
"""Describe a point based on its coordinates."""
|
|
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) if x == y:
|
|
return f"On diagonal at ({x}, {y})"
|
|
case (x, y):
|
|
return f"Point at ({x}, {y})"
|
|
case _:
|
|
return "Not a valid point"
|
|
|
|
print("\n--- Matching Sequences ---")
|
|
print(describe_point((0, 0)))
|
|
print(describe_point((0, 5)))
|
|
print(describe_point((3, 0)))
|
|
print(describe_point((4, 4)))
|
|
print(describe_point((3, 7)))
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 5. Matching with Variable Length Sequences
|
|
# -----------------------------------------------------------------------------
|
|
# Use * to capture remaining elements
|
|
|
|
def analyze_list(data):
|
|
"""Analyze list structure."""
|
|
match data:
|
|
case []:
|
|
return "Empty list"
|
|
case [single]:
|
|
return f"Single element: {single}"
|
|
case [first, second]:
|
|
return f"Two elements: {first} and {second}"
|
|
case [first, *middle, last]:
|
|
return f"First: {first}, Last: {last}, Middle count: {len(middle)}"
|
|
case _:
|
|
return "Not a list"
|
|
|
|
print("\n--- Variable Length Matching ---")
|
|
print(analyze_list([]))
|
|
print(analyze_list([42]))
|
|
print(analyze_list([1, 2]))
|
|
print(analyze_list([1, 2, 3, 4, 5]))
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 6. Matching Dictionaries
|
|
# -----------------------------------------------------------------------------
|
|
# Match against dictionary patterns
|
|
|
|
def process_request(request):
|
|
"""Process different types of requests."""
|
|
match request:
|
|
case {"type": "login", "user": username}:
|
|
return f"User '{username}' is logging in"
|
|
case {"type": "logout", "user": username}:
|
|
return f"User '{username}' is logging out"
|
|
case {"type": "message", "user": username, "content": content}:
|
|
return f"Message from '{username}': {content}"
|
|
case {"type": action}:
|
|
return f"Unknown action: {action}"
|
|
case _:
|
|
return "Invalid request format"
|
|
|
|
print("\n--- Matching Dictionaries ---")
|
|
print(process_request({"type": "login", "user": "Baraa"}))
|
|
print(process_request({"type": "message", "user": "Ali", "content": "Hello!"}))
|
|
print(process_request({"type": "logout", "user": "Sara"}))
|
|
print(process_request({"type": "unknown_action"}))
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 7. Matching Class Instances
|
|
# -----------------------------------------------------------------------------
|
|
# Match against object attributes
|
|
|
|
class Point:
|
|
"""Simple point class for demonstration."""
|
|
def __init__(self, x, y):
|
|
self.x = x
|
|
self.y = y
|
|
|
|
def classify_point(point):
|
|
"""Classify point using class matching."""
|
|
match point:
|
|
case Point(x=0, y=0):
|
|
return "At origin"
|
|
case Point(x=0, y=y):
|
|
return f"On Y-axis at {y}"
|
|
case Point(x=x, y=0):
|
|
return f"On X-axis at {x}"
|
|
case Point(x=x, y=y):
|
|
return f"At ({x}, {y})"
|
|
case _:
|
|
return "Not a Point"
|
|
|
|
print("\n--- Matching Class Instances ---")
|
|
print(classify_point(Point(0, 0)))
|
|
print(classify_point(Point(0, 7)))
|
|
print(classify_point(Point(5, 0)))
|
|
print(classify_point(Point(3, 4)))
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 8. Practical Example: Command Parser
|
|
# -----------------------------------------------------------------------------
|
|
# Real-world use case for a simple command interpreter
|
|
|
|
def execute_command(command):
|
|
"""Parse and execute simple commands."""
|
|
parts = command.split()
|
|
|
|
match parts:
|
|
case ["quit"] | ["exit"] | ["q"]:
|
|
return "Goodbye!"
|
|
case ["hello"]:
|
|
return "Hello there!"
|
|
case ["hello", name]:
|
|
return f"Hello, {name}!"
|
|
case ["add", x, y]:
|
|
return f"Result: {int(x) + int(y)}"
|
|
case ["repeat", count, *words]:
|
|
return " ".join(words) * int(count)
|
|
case ["help"]:
|
|
return "Available: quit, hello [name], add x y, repeat n words..."
|
|
case [unknown, *_]:
|
|
return f"Unknown command: {unknown}. Type 'help' for assistance."
|
|
case _:
|
|
return "Please enter a command"
|
|
|
|
print("\n--- Command Parser Example ---")
|
|
print(f"'hello': {execute_command('hello')}")
|
|
print(f"'hello Baraa': {execute_command('hello Baraa')}")
|
|
print(f"'add 5 3': {execute_command('add 5 3')}")
|
|
print(f"'repeat 3 Hi': {execute_command('repeat 3 Hi ')}")
|
|
print(f"'help': {execute_command('help')}")
|
|
print(f"'xyz': {execute_command('xyz')}")
|