We just upgraded our main repo from Python 3.9. Pattern matching in Python, introduced in Python 3.10 with the match
statement, can be particularly useful in cases where you have complex branching logic based on the structure of data. It takes a page out of Scala / other functional languages – here’s an example to illustrate the improvement:
Before: Using if-elif-else
Let’s consider a scenario where you’re working with a simple abstract syntax tree (AST) for arithmetic expressions. The tree can contain operations like addition, multiplication, or just a number.
def evaluate(expr):
if isinstance(expr, tuple) and len(expr) == 3:
op, left, right = expr
if op == '+':
return evaluate(left) + evaluate(right)
elif op == '*':
return evaluate(left) * evaluate(right)
else:
raise ValueError(f"Unknown operator: {op}")
elif isinstance(expr, (int, float)):
return expr
else:
raise ValueError(f"Invalid expression: {expr}")
# Example usage
expr = ('+', ('*', 2, 3), 4) # Represents (2 * 3) + 4
print(evaluate(expr)) # Output: 10
- Verbosity: The
if-elif-else
approach requires multiple checks for both the type and structure of the input. - Nested conditions: You have to manually unpack and check the contents of tuples and ensure that the structure is what you expect.
- Error-prone: The likelihood of missing a case or making an error in the logic is higher.
After: Using Pattern Matching
With pattern matching, the code becomes more readable and concise:
def evaluate(expr):
match expr:
case (op, left, right) if op == '+':
return evaluate(left) + evaluate(right)
case (op, left, right) if op == '*':
return evaluate(left) * evaluate(right)
case int() | float() as number:
return number
case _:
raise ValueError(f"Invalid expression: {expr}")
# Example usage
expr = ('+', ('*', 2, 3), 4) # Represents (2 * 3) + 4
print(evaluate(expr)) # Output: 10
- Conciseness: Pattern matching reduces the verbosity by allowing you to specify the structure and conditions directly in the
match
statement. - Readability: It’s easier to understand what each case does at a glance.
- Extensibility: Adding new patterns or handling additional cases can be done more straightforwardly.
I’ll admit, I don’t normally reach for pattern matching immediately – it comes more as a refactoring step after-the-fact, but there’s definitely a certain elegance to it and I’ll be looking to use to more in the future.