# Structural Pattern Matching

## Pattern Matching example 1

`def analyze_input_from_user():    text = input("Enter some text: ")    match text.split():        case []:            print("Got nothing")        case [("add" | "subtract") as cmd, a, b]:            print(f"Got operation {cmd} with parameters {a} and {b}")        case [x] if x.isdigit():            number = int(x)            print(f"Got a single integer number: {number}")        case [x] if re.match(r"^[+-]?\\d+(.\\d*)?\$", x):            number = float(x)            print(f"Got a single floating point number: {number:.3f}")        case [word]:  # Must be non-numeric, since numerics are matched above            print(f"Got a single non-numeric word: {word}")        case _:            print("Got something else")`
`def analyze_input_from_user_the_old_way():    text = input("Enter some text: ")    words = text.split()    if len(words) == 0:        print("Got nothing")    elif len(words) == 3 and words in ["add", "subtract"]:        (cmd, a, b) = words        print(f"Got operation {cmd} with parameters {a} and {b}")    elif len(words) == 1 and words.isdigit():        number = int(words)        print(f"Got a single integer number: {number}")    elif len(words) == 1 and re.match(r"^[+-]?\\d+(.\\d*)?\$", words):        number = float(words)        print(f"Got a single floating point number: {number:.3f}")    elif len(words) == 1:        word = words        print(f"Got a single non-numeric word: {word}")    else:        print("Got something else")`

## Pattern matching example 2

`issues = [    {        'issuer': "Mr Praline",        'date': date(1969, 12, 7),        'type': 'complaint',        'title': "a Dead Parrot",    },    {        'issuer': "A man",        'date': date(1972, 11, 2),        'type': 'request',        'subject': "an argument",        'location': "the Argument Clinic",    },    "Now something completely different",]def print_info_about_issues():    for issue in issues:        print_info_about_issue(issue)`
`def print_info_about_issue(issue):    match issue:        case {'type': 'complaint', 'date': d, 'title': t, 'issuer': i}:            print(f"{i} complained about {t} on {d}.")        case {            'type': 'request',            'date': date(year=y),            'subject': s,            'location': l,            'issuer': i,        }:            print(f"{i} requested to have {s} on {y} at {l}.")        case str(text):            print(f"Textual issue: {text}")`
`def print_info_about_issue_the_old_way(issue):    if isinstance(issue, dict) and (            issue.get('type') == 'complaint' and            'date' in issue and 'title' in issue and 'issuer' in issue):        d = issue['date']        t = issue['title']        i = issue['issuer']        print(f"{i} complained about {t} on {d}.")    elif isinstance(issue, dict) and (            issue.get('type') == 'request' and            isinstance(issue.get('date'), date) and            'subject' in issue and 'location' in issue and 'issuer' in issue):        y = issue['date'].year        s = issue['subject']        l = issue['location']        i = issue['issuer']        print(f"{i} requested to have {s} on {y} at {l}.")    elif isinstance(issue, str):        print(f"Textual issue: {issue}")`

# Typing related improvements

## Writing Union types as X | Y

`from decimal import Decimaldef format_euros(value: float | Decimal | None) -> str | None:    if value is None:        return None    return '{:.2f} €'.format(value)from decimal import Decimalfrom typing import Optional, Uniondef format_euros_old(value: Optional[Union[float, Decimal]]) -> Optional[str]:    if value is None:        return None    return '{:.2f} €'.format(value)`
`assert isinstance(3.5, float | None)assert issubclass(bool, int | str)  # since bool is subclass of intassert not isinstance("3.10", float | int | bytes)`

## Parameter Specification Variables

`from typing import Callable, ParamSpec, TypeVarP = ParamSpec("P")R = TypeVar("R")measured_times: dict[str, float] = {}def time_measured(f: Callable[P, R]) -> Callable[P, R]:    def inner(*args: P.args, **kwargs: P.kwargs) -> R:        t0 = time.perf_counter()        result = f(*args, **kwargs)        t1 = time.perf_counter()        measured_times[f.__name__] = t1 - t0        return result    return inner@time_measureddef repeated_string(a: str, n: int) -> str:    return a * nrepeated_string("ABC", 123)  # This is OKrepeated_string(123, "ABC")  # This should be rejected by type checker`

## Explicit Type Aliases

`TreeMap = "dict[str, Tree]"class Tree:    def __init__(self, subtrees: TreeMap) -> None:        self.subtrees = subtrees`
`from typing_extensions import TypeAliasTreeMap: TypeAlias = "dict[str, Tree]"class Tree:    def __init__(self, subtrees: TreeMap) -> None:        self.subtrees = subtrees`