Python 3.10 beta 3 is out, and while the release candidate phase is approaching, one feature has dominated the conversation: structural pattern matching via the new match/case statements (PEP 634, 635, and 636). Having spent the past few weeks experimenting with the beta, I’m convinced this is the most consequential addition to Python’s syntax since f-strings landed in 3.6 — and possibly since generators in 2.3.
If you’ve been following the PEP discussions, you know this feature generated significant controversy. Python has always prided itself on “one obvious way to do things,” and critics argue that pattern matching overlaps with existing if/elif chains. After actually using it, I think they’re wrong. Let me explain why.
Beyond Switch Statements#
The most common misconception is that match/case is Python’s version of C’s switch statement. It’s not. That framing completely undersells what structural pattern matching brings to the table.
Yes, you can use it for simple value matching:
match status_code:
case 200:
handle_success()
case 404:
handle_not_found()
case _:
handle_other()But that’s the least interesting use case. The real power emerges when you match on structure — decomposing complex data types and binding variables in a single, readable expression:
match command:
case {"action": "move", "direction": str(d), "distance": int(n)}:
move(d, n)
case {"action": "resize", "width": int(w), "height": int(h)}:
resize(w, h)
case {"action": "quit"}:
shutdown()This is matching on the shape of a dictionary, extracting typed values, and binding them to variables — all in one statement. Try doing that cleanly with if/elif chains. You’ll end up with nested conditionals, isinstance() calls, and temporary variables scattered everywhere.
Where Pattern Matching Shines#
After working with it for a few weeks, I’ve found three areas where pattern matching dramatically improves code quality.
AST and tree processing: If you work with parsed data structures — JSON APIs, configuration files, compiler internals, DOM-like trees — pattern matching is transformative. Walking a tree structure with pattern matching reads almost like a formal grammar specification:
match node:
case BinaryOp(left=Expression() as l, op="+", right=Literal(value=0)):
return l # x + 0 optimization
case BinaryOp(left=Literal(value=0), op="+", right=Expression() as r):
return r # 0 + x optimization
case BinaryOp(left=l, op=op, right=r):
return BinaryOp(optimize(l), op, optimize(r))Protocol/message handling: Any system that receives messages with varying structures — network protocols, event-driven architectures, command parsers — benefits enormously. Pattern matching replaces the kind of defensive, shape-checking code that accumulates in these handlers.
State machines: Matching on (current_state, event) tuples produces state machine implementations that are almost self-documenting:
match (state, event):
case (State.IDLE, Event.START):
return State.RUNNING
case (State.RUNNING, Event.PAUSE):
return State.PAUSED
case (State.RUNNING, Event.COMPLETE):
return State.DONEThe Guard Clause Addition#
One feature that doesn’t get enough attention is guard clauses — the ability to add if conditions to case patterns:
match point:
case Point(x, y) if x == y:
print("On the diagonal")
case Point(x, y) if x > 0 and y > 0:
print("First quadrant")Guards bridge the gap between structural matching and conditional logic. Without them, you’d still need nested if statements inside case blocks for anything beyond simple structure matching. With them, pattern matching handles the full range of dispatch logic.
The Controversy: Is This Pythonic?#
The Python community has been divided on this feature. The core argument against it: Python has always been readable to newcomers, and match/case introduces concepts (destructuring, binding, guards) that raise the learning curve.
I understand the concern, but I think it’s misplaced. Python already has complex features — list comprehensions, decorators, context managers, async/await. Each one raised the learning curve, and each one made Python a better language because they replaced patterns that were more verbose and error-prone.
The real question isn’t whether pattern matching is simple — it’s whether the code it replaces was simple. And in my experience, the if/elif/isinstance chains that pattern matching replaces are anything but simple. They’re just familiar, which isn’t the same thing.
There’s also the “soft keyword” implementation detail worth noting: match and case are not reserved keywords. They’re context-sensitive, meaning existing code that uses match or case as variable names will continue to work. This was a pragmatic decision that avoids breaking existing codebases — the kind of careful backward compatibility that I’ve always appreciated about Python’s evolution.
Performance Considerations#
One question I’ve been investigating is performance. Pattern matching isn’t just syntactic sugar over if/elif — the compiler can potentially optimize the dispatch. In the current beta, the performance is roughly comparable to equivalent if/elif chains for simple cases, and marginally better for complex structural matching because it avoids redundant attribute access.
That said, this is beta software, and I expect the CPython team will continue optimizing the bytecode generation for pattern matching in subsequent releases. The important thing is that it’s not slower than the alternative, which removes the performance objection.
My Take#
I’ve been writing Python since the 1.5 days, and I’ve watched every major syntax addition with a mix of excitement and skepticism. Pattern matching earned my enthusiasm faster than most.
The key insight is that this feature doesn’t add a new way to do something Python could already do easily. It adds a clean way to do something Python could only do clumsily. Destructuring nested data, dispatching on type and structure simultaneously, expressing complex matching logic declaratively — these are all things I’ve written ugly code to accomplish for years.
If you’re on a team that processes structured data, handles heterogeneous messages, or implements anything resembling a protocol parser, start experimenting with the 3.10 beta now. The migration path for existing if/elif dispatch code is straightforward, and the readability improvement is immediate.
Python 3.10 isn’t just an incremental release. Structural pattern matching is a genuine leap forward in how we express logic in Python, and I suspect it’ll become as natural as list comprehensions within a couple of years.
