"""
Q: What is pattern matching in Python?

A: Pattern matching in Python allows you to match data structures against specific patterns, enabling more readable and concise code for handling different data shapes. It provides functionalities similar to switch-case statements found in other languages.

Q: How does Python's pattern matching compare to switch-case statements in languages like C#?

A: Python's match-case construct is similar to switch-case in that it allows branching based on the value of a variable. However, match-case is more powerful, supporting complex patterns, variable bindings, and guards, which go beyond simple alue comparisons.

Q: What is the basic syntax of the match-case construct in Python?

A: 

```
match value:
    case pattern1:
        # code for pattern1
    case pattern2:
        # code for pattern2
```

match initiates the pattern matching on value. Each case is evaluated from top to bottom. Only the first matching case is executed, with no fall-through.

Q: Does Python's match-case construct support fall-through behavior like some other languages' switch-case?

A: No, Python's match-case executes only the first matched case and does not support fall-through behavior. Once a case is matched and executed, the control exits the match block.

Q: What will be the output of the following code?

```
match 1:
    case 1:
        print("First case")
    case 1:
        print("Second case")
```

A: The output will be:

```
First case
```

The second case is never reached because the first `case 1:` matches and executes first.

Q: How can you perform literal value matching using match-case in Python?

A: You can match against specific literal values directly in a case statement:

```
match value:
    case literal_value:
        # Handle the literal value
```

Q: Provide an example of a function using literal value pattern matching.

A: 

```
def describe_type(value):
    match value:
        case 0:
            return "Zero"
        case b"hello":
            return "Greeting"
        case True:
            return "Boolean True"
        case None:
            return "None value"
```

This function returns a description based on the literal value of `value`.

Q: How are Enums matched in Python's pattern matching?

A: Enums are inherently literal values and can be matched directly in case statements.

Q: Provide an example of matching Enum values using match-case.

A: 

```
from enum import Enum, auto

class HttpStatus(Enum):
    OK = auto()
    NOT_FOUND = auto()
    SERVER_ERROR = auto()

def handle_response(status):
    match status:
        case HttpStatus.OK:
            return "Request successful"
        case HttpStatus.NOT_FOUND:
            return "Resource not found"
        case HttpStatus.SERVER_ERROR:
            return "Internal server error"
        case _:
            return "Unknown status"

print(handle_response(HttpStatus.OK))          # Output: Request successful
print(handle_response(HttpStatus.NOT_FOUND))    # Output: Resource not found
```

This function matches different `HttpStatus` Enum values and returns corresponding messages.

Q: What is variable binding in the context of Python's pattern matching?

A: Variable binding allows you to capture and bind values within matched patterns to variables, enabling further processing or condition checking within the matched case.

Q: How do capture patterns work in match-case?

A: Using `case variable:` binds any matched value to the specified variable. This acts as a wildcard that matches any value but also captures it for use within the case block.

Q: Provide an example of using a capture pattern with an if-guard.

A: 

```
match "foo":
    case bar if bar.startswith("f"):
        print(bar)  # Outputs: foo
    case bar:
        ...
```

Q: What happens if a pure capture pattern is not the last pattern in a match-case statement?

A: If a pure capture pattern (without an if-guard) is not the last pattern, Python will raise a `SyntaxError` indicating that remaining patterns are unreachable.

Q: Explain variable scope when using captured variables in match-case.

A: Captured variables in case patterns are accessible in the outer scope, not just within the case block. This is different from some other languages where variables are limited to the block scope.

Example:

```
def test():
    match 42:
        case value:
            pass
    print(value)  # Outputs: 42

test()
```

Q: Can you reuse variable names in nested patterns? If so, how?

A: No, you cannot reuse variable names at the same nesting level except in or-patterns. Attempting to bind the same variable name multiple times in the same pattern leads to a `SyntaxError`.

Invalid Example:

```
case (x, x):  # Error
```

Valid Example with Or-Patterns:

```
case (1, x) | (x, 3):  # `x` is bound correctly
```

Q: What is an if-guard in Python's pattern matching?

A: An if-guard adds an additional condition to a case pattern, ensuring that the case only matches if the condition evaluates to True.

Q: How is an if-guard used in a case statement?

A: 

```
case pattern if condition:
    # code to execute if pattern matches and condition is True
```

Q: Provide an example using an if-guard with a logical and.

A: 

```
case n if n > 0 and n == 42:
    return (n, "god-liked")
```

Q: Can if-guards access variables from nested patterns?

A: Yes, if-guards have access to variables defined in deeply nested patterns within the case.

Q: How are multiple conditions handled in if-guards?

A: You can combine multiple conditions using logical operators (`and`, `or`, `not`) within the if-guard.

Example:

```
case [_, (x, y)] if x > 0 and y == 42:
    return (x, y, "Special Match")
```

Q: How can capture patterns and if-guards be used to implement if-else logic within a match statement?

A: They can replicate if-else logic by specifying different cases with and without conditions, handling various scenarios similarly to if-elif-else chains.

Example:

```
def process_command(command, current_room):
    match command.split():
        case ["go", direction] if direction in current_room.exits:
            return current_room.move(direction)
        case ["go", _]:
            return "Invalid direction. You can't go that way."
        case ["look"]:
            return current_room.description
        case ["inventory"]:
            return player.inventory_list()
        case _:
            return "Unknown command. Try 'go', 'look', or 'inventory'."
```

This function processes different commands based on their structure and content.

Q: What does the wildcard pattern _ represent in match-case?

A: The wildcard pattern `_` matches any value without binding it to a name. It's useful for default cases or when certain parts of a pattern are irrelevant.

Q: Can the wildcard _ be used multiple times in a pattern?

A: Yes, the wildcard `_` can be repeated in sequence patterns to match multiple elements without binding them.

Example:

```
case [_, _]:  # Matches any list with exactly two elements
```

Q: Can you bind the wildcard _ to a variable?

A: No, the wildcard `_` is not a valid target for binding and cannot be assigned to a variable. Attempting to access `_` will result in an error.

Invalid Example:

```
case _:
    print(_)  # Error: name '_' is not defined
```

Q: How is the wildcard _ used as a default case in match-case?

A: 

```
match value:
    case pattern1:
        ...
    case pattern2:
        ...
    case _:
        # Default case
```

The wildcard `_` ensures that any value not matched by previous patterns is handled here.

Q: Can the wildcard _ be used as a key in dictionary patterns?

A: In dictionary patterns, wildcards can be used to ignore values but not keys. Using `_` as a key is invalid.

Valid Example:

```
case {"known_key": _}:
    # Matches any value for "known_key"
```

Invalid Example:

```
case {_: "known_value"}:  # Error: Can't use wildcard as a key
```

Q: How are splat operators * and ** used with the wildcard _?

A: 

`*` and `**` can be combined with `_` to ignore multiple elements in sequences or key-value pairs in dictionaries.

Examples:

```
case [first, *_, last]:
    return f"First: {first}, Last: {last}"
case {"mode": "production", **_}:
    return "Production mode"
```

Q: What is partial matching in pattern matching?

A: Partial matching combines fixed values with wildcards or capture patterns to match only parts of a data structure, allowing flexibility in handling various data shapes.

Q: Provide an example of a sequence pattern with partial matching.

A: 

```
match data:
    case [1, x, _]:
        print(f"Second element is {x}")
```

This matches a sequence starting with `1`, captures the second element as `x`, and ignores the rest.

Q: How can dictionary patterns be used for partial matching?

A: 

```
match user:
    case {"name": str() as name, "age": int()}:
        print(f"{name}'s age is an integer")
```

This pattern matches dictionaries with specific keys and types, allowing other keys to be present but not affecting the match.

Q: Can class patterns be used for partial matching? Provide an example.

A: 

```
match point:
    case Point(0, y):
        print(f"On y-axis at {y}")
```

This matches `Point` objects where the first attribute (`x`) is `0`, effectively checking if the point lies on the y-axis.

Q: What is a nested pattern in partial matching? Provide an example.

A: Nested patterns allow matching within nested structures such as dictionaries inside lists or other complex combinations.

Example:

```
match config:
    case {"database": {"host": "localhost", "port": port}}:
        print(f"Local database on port {port}")
```

This matches a nested dictionary structure, extracting the port value.

Q: What are sequence patterns in Python's pattern matching?

A: Sequence patterns match sequences (like lists or tuples) by unpacking their elements and optionally binding them to variables. They can also enforce length constraints.

Q: How does length validation work in sequence patterns?

A: Sequence patterns can specify exact lengths by the number of patterns. For variable lengths, splat operators (`*`) can be used to capture remaining elements.

Examples:

```
case []:                     # Matches empty sequences
case [_, _, _]:             # Matches sequences with exactly 3 elements
case [x, y, z, *rest]:      # Matches sequences with at least 3 elements
```

Q: Which Python types are compatible with sequence patterns?

A: 

- Classes inheriting from `collections.abc.Sequence`: `list`, `tuple`, `range`, `memoryview`, `deque`, `array`.
- Built-in classes with the `Py_TPFLAGS_SEQUENCE` flag: `array.array`, `collections.deque`, `list`, `memoryview`, `range`, `tuple`.

**Note:** Exceptions like `str`, `bytes`, and `bytearray` use literal matching instead of sequence unpacking.

Q: Are brackets mandatory in sequence patterns?

A: Brackets are optional except when matching an empty sequence. Both `[]` and `()` can be used for unpacking.

Examples:

```
case x, y:
    return f"2D point: ({x}, {y})"
case (x, y, z):
    return f"3D point: ({x}, {y}, {z})"
```

Q: How is the splat operator * used in sequence patterns?

A: The splat operator `*` allows variable-length unpacking, capturing any number of additional elements into a list.

**Rules:**

- Only one splat operator per sequence pattern.
- Can appear at any position within the pattern.

**Examples:**

```
case x, y, z, *rest:
    return f"Elements: {x}, {y}, {z}, {rest}"
case first, *middle, last:
    return f"First: {first}, Middle: {middle}, Last: {last}"
```

Q: How do you match an empty sequence using match-case?

A: 

```
case []:
    # Handle empty sequence
```

Q: How do you handle sequences of specific lengths in pattern matching?

**Length-1 Sequence:**

```
case [_]:
case _,:
```

**Fixed Length Matching:**

```
case [_, _, _]:  # Matches exactly 3 elements
```

**Variable Length Matching:**

```
case [*_] as sequence if len(sequence) > 2:
```

Q: What pitfalls might you encounter when using simple length checks in if-guards?

A: Using `len()` directly in if-guards can cause errors if the target doesn't have a `__len__` method. To avoid this, ensure the target is an instance of `collections.abc.Sequence` before checking its length.

**Problematic Example:**

```
case seq if len(seq) > 2:  # Error if seq has no __len__
```

**Corrected Example:**

```
case seq if isinstance(seq, collections.abc.Sequence) and len(seq) > 2:
```

Q: How does sequence pattern matching reduce code complexity compared to manual unpacking?

A: Pattern matching eliminates the need for explicit length checks and index-based access, allowing you to handle different input patterns structurally and declaratively. This leads to more readable and maintainable code.

**Comparison Example:**

**Manual Unpacking:**

```
parts = command.split()
if len(parts) == 2:
    action, obj = parts
    # interpret action, obj
elif len(parts) >= 1:
    action = parts[0]
    # handle action with variable arguments
```

**Pattern Matching:**

```
match command.split():
    case [action, obj]:
        # interpret action, obj
    case [action, *args]:
        # handle action with variable arguments
    case _:
        # handle invalid input
```

Q: What are mapping patterns in match-case?

A: Mapping patterns are used to match dictionaries by specifying key-value pairs. They allow you to match specific keys and bind their corresponding values.

Q: What is the basic syntax for a mapping pattern that matches all dictionaries?

A: 

```
case {}:
    # Matches any dictionary
```

Q: How do you match specific keys and bind their values in a dictionary pattern?

A: 

```
case {"key1": pattern1, "key2": pattern2, **rest}:
    # Handle matched patterns
```

This matches dictionaries containing `key1` and `key2`, binds their values to `pattern1` and `pattern2`, and captures any remaining key-value pairs in `rest`.

Q: Provide an example of a dictionary pattern that binds values to variables.

A: 

```
case {"name": name, "age": age}:
    return f"{name} is {age} years old"
```

This pattern matches dictionaries with `name` and `age` keys, binding their values to `name` and `age` variables.

Q: How can you capture remaining key-value pairs in a dictionary pattern?

A: Use the `**` operator to capture any additional key-value pairs.

**Example:**

```
case {"name": name, **rest}:
    return f"Name: {name}" + (f", extras: {rest}" if rest else "")
```

This captures any keys beyond `name` in the `rest` dictionary.

Q: How are keys and values compared in dictionary patterns?

A: 

- Keys are compared using the `==` operator.
- Key-value pairs are matched using the `get()` method.

Q: Which types are supported for mapping patterns?

A: 

- Classes inheriting from `collections.abc.Mapping`: `dict`, `types.MappingProxyType`, `collections.defaultdict`, `collections.OrderedDict`, `collections.Counter`, `collections.ChainMap`, `types.SimpleNamespace`.
- Built-in classes with the `Py_TPFLAGS_MAPPING` flag: `dict`, `mappingproxy`.

Q: How do you match both empty and non-empty dictionaries using pattern matching?

**Empty Dictionary:**

```
case {}:
```

**Non-Empty Dictionary using if-guard:**

```
case {} as dict if dict:
    # Non-empty dictionary
case {} as dict if not dict:
    # Empty dictionary
```

Q: Can you provide a practical example of processing user actions with mapping patterns?

A: 

```
def process_actions(actions, ui):
    for action in actions:
        match action:
            case {"type": "message", "text": msg, "color": color}:
                ui.display_message(msg, color)
            case {"type": "wait", "duration": duration}:
                ui.wait(duration)
            case {"type": "sound", "url": url, "format": "ogg"}:
                ui.play_sound(url)
            case {"type": "sound", "format": format}:
                print(f"Unsupported audio format: {format}")
            case _:
                print(f"Unknown action: {action}")
```

This function processes a list of action dictionaries, handling different action types appropriately.

Q: What is an or-pattern in Python's pattern matching?

A: An or-pattern allows matching multiple alternative patterns within a single case statement, separated by the `|` symbol. It functions as a logical OR.

Q: How do you write an or-pattern in a case statement?

A: 

```
case pattern1 | pattern2 | ...:
    # Code to execute if any pattern matches
```

Q: Provide an example of using an or-pattern with literal values.

A: 

```
match value:
    case "foo" | "bar":
        print("Value is either 'foo' or 'bar'")
```

Q: Are single-value or-patterns useful? Why or why not?

A: Single-value or-patterns are generally not useful because using multiple identical patterns like `case foo | foo:` is redundant. Instead, a single `foo` is sufficient.

Q: Can different variables be bound in each sub-pattern of an or-pattern? Explain with an example.

A: No, all sub-patterns in an or-pattern must bind the same variable names at identical structural positions.

Invalid Example:

```
case foo | bar:  # Error if foo and bar bind different variables
    ...
```

Valid Example:

```
case ["get", obj] | ["pick", "up", obj]:
    print(f"Action: {obj}")
```

Q: How are or-patterns used with structural patterns like lists or dictionaries?

A: Or-patterns can combine structural patterns, ensuring that any of the alternative structures can be matched.

**Example:**

```
case [1, x] | [x, 3]:
    # Matches a list starting with 1 or ending with 3
```

Q: Can you combine or-patterns with if-guards? How?

A: Yes, or-patterns can be combined with if-guards. The if-guard applies to the entire or-pattern.

**Example:**

```
case pattern1 | pattern2 if condition:
    # Code executes if either pattern1 or pattern2 matches and condition is True
```

Q: Provide a practical example using or-patterns in shape processing.

A: 

```
def process_shape(shape):
    match shape:
        # Circles or squares with the same radius logic
        case ('circle', r) | ('square', r) if 1 <= r <= 10:
            area = 3.14 * r**2 if shape[0] == 'circle' else r**2
            print(f"{shape[0].capitalize()} with radius {r}")
            print(f"Area: {area:.2f}")
        
        # Rectangles or triangles with width and height
        case ('rectangle', w, h) | ('triangle', w, h) if 1 <= w <= 10 and 1 <= h <= 10:
            if shape[0] == 'rectangle':
                area = w * h
                shape_name = "Rectangle"
            else:
                area = 0.5 * w * h
                shape_name = "Triangle"
            print(f"{shape_name} {w}x{h}")
            print(f"Area: {area:.2f}")
        
        # Catch-all case
        case _:
            print("Invalid shape or dimensions out of range")
```

This function processes different shapes, handling circles, squares, rectangles, and triangles with specific conditions.

Q: What is a class pattern in Python's pattern matching?

A: A class pattern matches objects based on their class, allowing you to match both positional and keyword attributes of the class.

Q: What is the syntax for a class pattern that includes both positional and keyword patterns?

A: 

```
case ClassName(pos_pattern1, pos_pattern2, ..., keyword1=kw_pattern1, ...):
    # Code to execute if pattern matches
```

Positional patterns match the class attributes defined in `__match_args__`. Keyword patterns match specific attributes by name.

Q: How do class keyword patterns perform instance attribute-value matching?

A: Class keyword patterns check:

- The instance is of the specified class type.
- Each specified keyword (attribute name) matches the given pattern.

**Example:**

```
case Click(position=(x, y)) if y > 50:
    print(f"Clicked at ({x}, {y})")
```

Q: How are class positional patterns related to the __match_args__ attribute?

A: The `__match_args__` class attribute specifies the order of attributes to be matched positionally. Positional patterns correspond to these attributes, converting them internally to keyword patterns based on `__match_args__`.

**Example:**

```
class Point:
    __match_args__ = ("x", "y")
    
    def __init__(self, x, y):
        self.x = x
        self.y = y

def classify_point(point):
    match point:
        case Point(0, 0):
            return "Origin"
        case Point(0, y):
            return f"On Y-axis at y={y}"
        # More cases...
```

Q: How do dataclasses interact with class patterns in pattern matching?

A: Dataclasses automatically implement the `__match_args__` attribute, enabling easy use of class patterns without additional boilerplate.

**Example:**

```
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

match point:
    case Point(1, 2):
        print("Matched Point at (1, 2)")
    case Point(x, y):
        print(f"Matched Point at ({x}, {y})")
```

Q: Can built-in classes be used with class patterns? How?

A: Yes, several built-in classes define the first positional attribute to return the instance itself, allowing instance checking and binding directly.

**Example:**

```
case str(text):
    # Equivalent to case str() as text
```

Q: How can you customize the positional patterns of built-in classes?

A: By inheriting from built-in classes and defining the `__match_args__` attribute, you can customize their positional matching behavior.

**Example:**

```
import pathlib

class Path(pathlib.Path):
    __match_args__ = ("__match_self_prop__",)
    
    @property
    def __match_self_prop__(self):
        return self.__fspath__()

def process_path(path):
    match path:
        case str(p) | Path(p):
            return f"Processing path: {p}"
        case _:
            return "Not a valid path"
```

Q: Provide a practical example using class patterns for event handling.

A: 

```
from collections import namedtuple

Click = namedtuple('Click', ['position'])
KeyPress = namedtuple('KeyPress', ['key'])
Quit = namedtuple('Quit', [])

def handle_event(event):
    match event:
        case Click(position=(x, y)):
            print(f"Mouse clicked at ({x}, {y})")
        case KeyPress(key="Q") | Quit():
            print("Quitting application")
        case KeyPress(key="Up"):
            print("Moving north")
        case KeyPress():
            print("Key pressed, but not handled")
        case _:
            print(f"Unrecognized event: {event}")

# Test cases
handle_event(Click((100, 200)))        # Mouse clicked at (100, 200)
handle_event(KeyPress("Q"))           # Quitting application
handle_event(KeyPress("Up"))          # Moving north
handle_event(KeyPress("Left"))        # Key pressed, but not handled
handle_event(Quit())                  # Quitting application
handle_event("Unknown")               # Unrecognized event: Unknown
```

This function handles different event types using class patterns, printing appropriate messages based on the event.

Q: Can you combine class positional and keyword patterns in a single pattern? Provide an example.

A: Yes, you can combine positional and keyword patterns to match both the order and specific attribute values.

**Example:**

```
class Person:
    __match_args__ = ("name", "age")
    
    def __init__(self, name, age, city):
        self.name = name
        self.age = age
        self.city = city

def describe_person(person):
    match person:
        case Person(name, age, city="New York"):
            return f"{name}, {age} years old, lives in New York"
        case Person(name, age=30):
            return f"{name} is 30 years old"
        case Person(name):
            return f"Person named {name}"
```

Q: Provide an example of a class pattern with nested dictionary and sequence matching.

A: 

```
class Report:
    def __init__(self, title, data):
        self.title = title
        self.data = data

def analyze_report(report):
    match report:
        case Report(title=str(title), data={"summary": [int(total), int(average)]}):
            return f"Report '{title}': Total {total}, Average {average}"
        case Report(title=str(title), data={"details": [*items]}):
            return f"Detailed report '{title}' with {len(items)} items"
        case _:
            return "Invalid report format"
```

This function analyzes a `Report` object, matching its title and data attributes with nested dictionary and sequence patterns.

Q: What is the as pattern in Python's pattern matching?

A: The `as` pattern allows you to bind the entire matched pattern, sub-pattern, or literal value to a variable name, facilitating reuse of matched values within the case block or if-guards.

Q: What is the syntax for using the as pattern?

A: 

```
case pattern as variable_name:
    # Code to execute
```

Q: Can the as pattern be used with all types of patterns? Provide examples.

A: Yes, the `as` pattern can be used with sequence patterns, literal values, class patterns, dictionary patterns, and more.

**Examples:**

```
case [x, y] as point:
    # point is the entire list

case "foo" as foo if len(foo) > 1:
    # foo is the string "foo"

case Click() as ev:
    # ev is the Click instance

case {"foo": int() as foo}:
    # foo is the value associated with "foo" key
```

Q: Can the as pattern be used to bind to the wildcard _?

A: No, you cannot bind the as pattern to the wildcard `_`. Attempting to do so results in a syntax error.

Invalid Example:

```
case foo as _:
    # Error
```

Q: How does the as pattern enhance readability and reduce redundancy?

A: By binding the entire pattern or parts of it to a variable, you can reference it directly within the case block or if-guards without repeating the pattern, making the code cleaner and more maintainable.

Q: Provide an example using the as pattern to parse command-line arguments.

A: 

```
def parse_command(cmd):
    match cmd.split():
        case ["quit" | "exit" as action]:
            return f"Exiting program ({action})"
        case ["create", ("file" | "directory") as item_type, name]:
            return f"Creating {item_type}: {name}"
        case ["delete", path as target] if "/" in target:
            return f"Deleting file at path: {target}"
        case ["move", source, destination] as full_cmd:
            return f"Moving from {source} to {destination} (Full command: {' '.join(full_cmd)})"
        case _:
            return "Invalid command"
```

This function parses different command-line inputs, binding specific parts of the command to variables for further processing.

Q: What shorthand notations exist for the as pattern with primitive types?

A: Some built-in classes have shorthand notation for the `as` pattern, allowing you to omit the `as` keyword when binding primitive types.

**Example:**

```
case str(text): ...
# Equivalent to
case str() as text: ...
```

This shorthand is supported for classes like `bool`, `bytearray`, `bytes`, `dict`, `float`, `frozenset`, `int`, `list`, `set`, `str`, and `tuple`.

Q: Are patterns nestable in Python's pattern matching?

A: Yes, patterns can be nested within each other, allowing for complex matching scenarios involving multiple layers of data structures.

Q: What rules apply to variable bindings in nested patterns?

A: Variable bindings must be unique across any nesting level, except within or-patterns. Binding the same variable multiple times at the same nesting level (outside or-patterns) results in a `SyntaxError`.

Q: Provide an example of a sequence pattern with nested dictionary matching.

A: 

```
def process_weather_data(data):
    match data:
        case [{"city": str(city), "temp": int(temp)}, {"humidity": int(humidity)}]:
            return f"{city}: {temp}°C, Humidity: {humidity}%"
        case _:
            return "Invalid weather data"
```

This function matches a list containing two dictionaries with specific keys and types.

Q: How can you match nested class patterns within sequences? Give an example.

A: 

```
def process_library_data(data):
    match data:
        case [Book(title=str(title), author="Unknown"), *rest]:
            return f"First book: {title} by Unknown author"
        case [Book(title=str(title), author=str(author)), Book(title=str(title2), author=str(author2))]:
            return f"Two books: {title} by {author}, and {title2} by {author2}"
        case _:
            return "Invalid library data"
```

This function handles a list of `Book` objects, matching based on their titles and authors.

Q: Can you combine or-patterns with nested patterns? Provide an example.

A: Yes, or-patterns can be combined with nested patterns to match complex data structures with alternative configurations.

**Example:**

```
def process_data(data):
    match data:
        case [str(name), int(age)] | {"name": str(name), "age": int(age)}:
            return f"Name: {name}, Age: {age}"
        case _:
            return "Invalid data format"
```

This function matches either a list with a name and age or a dictionary with "name" and "age" keys.

Q: How are class patterns with nested dictionary and sequence matching used together?

A: Class patterns can include nested dictionary and sequence patterns to match attributes that themselves are complex data structures.

**Example:**

```
class Report:
    def __init__(self, title, data):
        self.title = title
        self.data = data

def analyze_report(report):
    match report:
        case Report(title=str(title), data={"summary": [int(total), int(average)]}):
            return f"Report '{title}': Total {total}, Average {average}"
        case Report(title=str(title), data={"details": [*items]}):
            return f"Detailed report '{title}' with {len(items)} items"
        case _:
            return "Invalid report format"
```

This function evaluates a `Report` object, extracting and processing nested data within its attributes.

Q: How can you use pattern matching to handle exceptions in Python?

A: While Python’s match-case doesn’t directly integrate with exception handling, you can use patterns within try-except blocks to match exception types and their attributes.

**Example:**

```
try:
    # Code that may raise exceptions
    x = 1 / 0
except Exception as e:
    match e:
        case ZeroDivisionError(msg):
            print("Cannot divide by zero!")
        case ValueError(msg):
            print("Value error encountered!")
        case _:
            print("An unexpected error occurred.")
```

Q: Can you use pattern matching with asynchronous code in Python? Provide an example.

A: Yes, pattern matching can be used within asynchronous functions to handle different asynchronous results.

**Example:**

```
async def fetch_data():
    # Simulate fetching data
    return {"status": "success", "data": [1, 2, 3]}

async def process_data():
    result = await fetch_data()
    match result:
        case {"status": "success", "data": data}:
            print(f"Data received: {data}")
        case {"status": "error", "message": msg}:
            print(f"Error: {msg}")
        case _:
            print("Unknown response")

# To run the asynchronous function
import asyncio
asyncio.run(process_data())
```

This asynchronous function uses pattern matching to handle different responses from `fetch_data()`.

Q: How do you integrate pattern matching with functional programming styles in Python?

A: Pattern matching can complement functional programming by enabling concise and expressive handling of different data structures. It allows for pattern-based transformations and operations without relying on mutable state.

**Example:**

```
def transform(data):
    match data:
        case {"type": "add", "values": [x, y]}:
            return x + y
        case {"type": "multiply", "values": [x, y]}:
            return x * y
        case _:
            return None

data_list = [
    {"type": "add", "values": [1, 2]},
    {"type": "multiply", "values": [3, 4]},
    {"type": "subtract", "values": [5, 2]},
]

transformed = list(map(transform, data_list))
print(transformed)  # Output: [3, 12, None]
```

This example uses `map` with a function that employs pattern matching to transform a list of dictionaries.

Q: Can pattern matching be used for destructuring data classes with inheritance? Provide an example.

A: Yes, pattern matching can destructure data classes that use inheritance by matching patterns specific to each subclass.

**Example:**

```
from dataclasses import dataclass

@dataclass
class Shape:
    pass

@dataclass
class Circle(Shape):
    radius: float

@dataclass
class Rectangle(Shape):
    width: float
    height: float

def describe_shape(shape):
    match shape:
        case Circle(radius=r):
            return f"Circle with radius {r}"
        case Rectangle(width=w, height=h) if w == h:
            return f"Square with side {w}"
        case Rectangle(width=w, height=h):
            return f"Rectangle with width {w} and height {h}"
        case _:
            return "Unknown shape"

# Test cases
print(describe_shape(Circle(radius=5)))        # Circle with radius 5
print(describe_shape(Rectangle(width=4, height=4)))  # Square with side 4
print(describe_shape(Rectangle(width=3, height=5)))  # Rectangle with width 3 and height 5
```

This function distinguishes between different subclasses of `Shape`, handling each appropriately.

Q: What are the key benefits of using pattern matching in Python?

A: 

- **Improved Readability:** Code becomes more declarative and easier to understand.
- **Conciseness:** Reduces boilerplate code associated with multiple if-elif-else statements.
- **Flexibility:** Supports complex patterns, enabling sophisticated data structure handling.
- **Safety:** Enforces exhaustive matching, reducing the risk of unhandled cases.

Q: When should you prefer pattern matching over traditional conditional statements?

A: 

- When dealing with multiple conditional branches based on the structure and content of data.
- When handling complex data structures like nested dictionaries, lists, or custom classes.
- To simplify code that would otherwise require extensive use of if-elif-else chains.
- When you need to destructure and bind variables from data structures efficiently.

Q: What are some common pitfalls to avoid when using pattern matching?

A: 

- **Unreachable Patterns:** Placing broad or capture patterns before more specific ones, causing the latter to never be matched.
- **Variable Name Conflicts:** Reusing variable names within the same or nested patterns, leading to `SyntaxError`.
- **Improper Use of Wildcards:** Attempting to bind or access the `_` wildcard, which is not allowed.
- **Overcomplicating Patterns:** Creating overly complex patterns that are hard to read and maintain.

Q: How can you ensure that all possible cases are handled in a match-case statement?

A: Always include a wildcard `_` case at the end of the match statement to handle any cases not explicitly matched by previous case statements. This ensures that all possible inputs are accounted for.

**Example:**

```
match value:
    case pattern1:
        ...
    case pattern2:
        ...
    case _:
        # Default case
```

Q: Can pattern matching be combined with other Python features like decorators or context managers? Provide an example if yes.

A: Yes, pattern matching can be used within functions that utilize decorators or context managers, enhancing the function's capabilities.

**Example with a Decorator:**

```
from functools import wraps

def log_match(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        print(f"Matched: {result}")
        return result
    return wrapper

@log_match
def describe(value):
    match value:
        case 0:
            return "Zero"
        case _:
            return "Non-zero"

describe(0)    # Output: Matched: Zero
describe(42)   # Output: Matched: Non-zero
```

**Example with a Context Manager:**

```
from contextlib import contextmanager

@contextmanager
def match_context(value):
    try:
        yield value
    finally:
        print("Match complete.")

def process(value):
    with match_context(value) as val:
        match val:
            case int():
                print("Integer value")
            case str():
                print("String value")
            case _:
                print("Other type")

process(10)   # Outputs: Integer value 
              #          Match complete.
process("hi") # Outputs: String value 
              #          Match complete.
```

Q: How can you use pattern matching to handle exceptions in Python?

A: While Python’s match-case doesn’t directly integrate with exception handling, you can use patterns within try-except blocks to match exception types and their attributes.

**Example:**

```
try:
    # Code that may raise exceptions
    x = 1 / 0
except Exception as e:
    match e:
        case ZeroDivisionError(msg):
            print("Cannot divide by zero!")
        case ValueError(msg):
            print("Value error encountered!")
        case _:
            print("An unexpected error occurred.")
```

Q: Can you use pattern matching with asynchronous code in Python? Provide an example.

A: Yes, pattern matching can be used within asynchronous functions to handle different asynchronous results.

**Example:**

```
async def fetch_data():
    # Simulate fetching data
    return {"status": "success", "data": [1, 2, 3]}

async def process_data():
    result = await fetch_data()
    match result:
        case {"status": "success", "data": data}:
            print(f"Data received: {data}")
        case {"status": "error", "message": msg}:
            print(f"Error: {msg}")
        case _:
            print("Unknown response")

# To run the asynchronous function
import asyncio
asyncio.run(process_data())
```

This asynchronous function uses pattern matching to handle different responses from `fetch_data()`.

Q: How do you integrate pattern matching with functional programming styles in Python?

A: Pattern matching can complement functional programming by enabling concise and expressive handling of different data structures. It allows for pattern-based transformations and operations without relying on mutable state.

**Example:**

```
def transform(data):
    match data:
        case {"type": "add", "values": [x, y]}:
            return x + y
        case {"type": "multiply", "values": [x, y]}:
            return x * y
        case _:
            return None

data_list = [
    {"type": "add", "values": [1, 2]},
    {"type": "multiply", "values": [3, 4]},
    {"type": "subtract", "values": [5, 2]},
]

transformed = list(map(transform, data_list))
print(transformed)  # Output: [3, 12, None]
```

This example uses `map` with a function that employs pattern matching to transform a list of dictionaries.

Q: Can pattern matching be used for destructuring data classes with inheritance? Provide an example.

A: Yes, pattern matching can destructure data classes that use inheritance by matching patterns specific to each subclass.

**Example:**

```
from dataclasses import dataclass

@dataclass
class Shape:
    pass

@dataclass
class Circle(Shape):
    radius: float

@dataclass
class Rectangle(Shape):
    width: float
    height: float

def describe_shape(shape):
    match shape:
        case Circle(radius=r):
            return f"Circle with radius {r}"
        case Rectangle(width=w, height=h) if w == h:
            return f"Square with side {w}"
        case Rectangle(width=w, height=h):
            return f"Rectangle with width {w} and height {h}"
        case _:
            return "Unknown shape"

# Test cases
print(describe_shape(Circle(radius=5)))        # Circle with radius 5
print(describe_shape(Rectangle(width=4, height=4)))  # Square with side 4
print(describe_shape(Rectangle(width=3, height=5)))  # Rectangle with width 3 and height 5
```

This function distinguishes between different subclasses of `Shape`, handling each appropriately.

Q: What is the overall impact of using pattern matching in Python programming?

A: Pattern matching significantly enhances the expressiveness, readability, and maintainability of Python code. It enables developers to handle complex data structures and conditional logic more naturally, reducing boilerplate and potential errors associated with traditional conditional statements.

Q: Are there any limitations or considerations to keep in mind when using pattern matching?

A: 

- **Performance:** While pattern matching can make code more readable, it may introduce slight overhead compared to simple conditionals.
- **Complexity:** Overusing deeply nested patterns can make the code harder to understand.
- **Exhaustiveness:** Ensuring all possible cases are handled is crucial to avoid unhandled scenarios.
- **Learning Curve:** Developers need to become familiar with the pattern matching syntax and best practices to use it effectively.

Q: How can one stay updated with the latest developments in Python's pattern matching?

A: 

- **Official Python Documentation:** Regularly review Python's official documentation.
- **PEP Proposals:** Follow Python Enhancement Proposals (PEPs), especially PEP 634, 635, and 636, which detail pattern matching.
- **Community Resources:** Engage with Python communities through forums, blogs, and tutorials.
- **Practice:** Implement pattern matching in projects and experiments to gain hands-on experience.
"""

Convert the Q&A into anki cards into format that can be imported by Anki, wrap it by triple tilde