ProgrammingPython Developer

How does the pattern matching mechanism (match-case) work in Python, what is its purpose, and what are its features?

Pass interviews with Hintsage AI assistant

Answer.

The pattern matching mechanism appeared in Python 3.10 under the name Structural Pattern Matching and is implemented using the match-case construct. This tool allows for elegantly and concisely analyzing complex data structures by matching them with patterns.

Background

In many functional languages (like Haskell, Scala), pattern matching has long been regarded as a convenient way to branch logic based on data structure. Python long lacked such a mechanism, relying on chains of if-elif-else or unpacking.

Problem

Complex nested data structures (dictionaries, lists, objects) often require multiple checks on types, values, and structure, leading to convoluted code with many conditions.

Solution

match-case provides a declarative and readable way to describe different data handling scenarios considering type and structure without an overload of conditions.

Example code:

def process(event): match event: case {"type": "click", "x": x, "y": y}: return f"Click at ({x}, {y})" case [command, *args]: return f"Command: {command}, args: {args}" case _: return "Unknown event" print(process({"type": "click", "x": 2, "y": 5})) # Click at (2, 5) print(process(["RUN", 1, 2, 3])) # Command: RUN, args: [1, 2, 3]

Key features:

  • Allows matching of both simple values and the structure of objects and collections
  • Convenient for parsing nested and heterogeneous data
  • Supports unpacking and type checks within case

Tricky questions.

Does match-case only match by value? No. Matching can occur by structure, type, unpacking collections, and even object properties (if special methods match_args are specified).

Does the order of cases in match matter? Yes. The analysis proceeds top to bottom, the first matching pattern triggers. Subsequent cases are not checked.

Can custom classes be matched? Yes, if the class implements match_args and/or getitem methods. Then pattern matching can extract field values.

Example:

class Point: __match_args__ = ("x", "y") def __init__(self, x, y): self.x = x self.y = y def where(obj): match obj: case Point(x, y): return f"Point at {x}, {y}" case _: return "Unknown" print(where(Point(1, 2))) # Point at 1, 2

Common mistakes and anti-patterns

Pros:

  • Code becomes more compact and expressive when parsing structures
  • Declarativeness is maintained, and scenarios are easy to expand Cons:
  • It can become confusing with too many matching patterns
  • There is a temptation to use match-case for simple conditions where it is unnecessary (overengineering)

Real-life example

Negative case: Developers used match-case even for simple boolean branches, which only complicated the code. Pros: they learned a new feature. Cons: they lost readability and increased the number of bugs.

Positive case: In an application with a large number of events and nested structures (e.g., a graph parser), match-case helped avoid cumbersome if-elif and centralized parsing. Pros: readability increased, and the number of bugs decreased. Cons: required training the team on the new construct.