Python Topics : Structural Pattern Matching (Switches)
What is Structural Pattern Matching?
structural pattern matching allows checking a value against a pattern
extract parts of the value if the pattern matches
can make complex conditionals simpler and more readable
basic syntax
#  -> str  indicates the function returns a string
def http_status(status: int) -> str:
    match status:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500:
            return "Internal Server Error"
        case _:
            return "Unknown Error"
patterns can range from simple constants to more complex structures
Constant Patterns
constant patterns match literal values
the underscore (_) is typically used as a placeholder when a particular value isn't important
_ used as default case below
value = 42

match value:
    case 0:
        print("Zero")
    case 3.14:
        print("Pi")
    case 42:
        print("The answer to life, the universe, and everything")
    case "Some Name":
        print("Hello")
                case _:
        print("Something Else")
Variable Patterns
variable patterns capture the matched value
when matching instances of any type, need to match against an object pattern
use the type_name() syntax similar to instantiating an object
value = 42

match value:
    case int() as i:
        print(f"Got an int variable: {i}")
    case float() as f:
        print(f"Got a float variable: {f}")
    case str() as s:
        print(f"Got a str variable: {s}")
    case unknown:
        print(f"Got an unknown object: unknown")
Sequence Patterns
sequence patterns match against lists, tuples, and other sequences
data = [1, 2, 3]

match data:
    case [1, 2, 3]:
        print("Matched [1, 2, 3]")
    case [1, 2, _]:
        print("Matched [1, 2, _]")
    case [int(), int(), int()]:
        print("Matched [int(), int(), int()]")
    case _ :
        print("No match")
Mapping Patterns
mapping patterns match against dictionaries
will only match against the keys defined in the match statement
ignores other keys
config = {"host": "localhost", "port": 8080, "token": ""}

match config:
    case {"host": host, "port": port}:
        print(f"Host: {host}, Port: {port}")
    case _:
        print("No match")
Conditional Patterns
patterns can match objects with optional extra evaluation
status = 404

match status:
    case int() if 0 <= status < 100:
        print("Informational")
    case int() if 100 <= status < 200:
        print("Success")
    case int() if 200 <= status < 300:
        print("Redirection")
    case int() if 300 <= status < 400:
        print("Client Error")
    case int() if 400 <= status < 500:
        print("Server Error")
    case _:
        print("Unknown error code")
Combining Patterns
patterns can be combined using | (OR)
status = 200

match status:
    case 200 | 201:
        print("Success")
    case 400 | 404:
        print("Client Error")
    case _:
        print("Other Error")
Add Matching to Custom Objects
to enable pattern matching on custom objects need to implement certain dunder methods in the classes
these methods help the match statement understand how to destructure the objects

the __match_args__ attribute is a tuple that specifies the attribute names for pattern matching
by default it's an empty tuple and no attributes can be extracted

class Point:
    __match_args__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y


point = Point(10, 20)

match point:
    case Point(10, 20):
        print("Home")
    case Point(x, y):
        print(f"Point with {x=}, {y=}")
    case _:
        print("Unknown Position")
index