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.

Categorized in: