Mastering Structural Pattern Matching
If you’re not familiar with the term Structural Pattern Matching then you are not alone. It’s a feature that, until about 10-15 years ago, you would not see outside of functional programming languages. Its use, however, has spread; today you can find a feature like it in C#, Swift, and Ruby. What was once the preserve of niche languages is now available for you to try in Python 3.10.
def greet_person(p): """Let's greet a person""" match p: case {"greeting": greeting, "name": name}: print(f"{greeting}, {name}") case {"name": name}: print(f"Hello, {name}!") case {"greeting": _} | {}: print("I didn't quite catch your name?") case str() as person if person.isupper(): print("No need to shout - I'm not deaf") case str() as person: print(f"Nice to meet you, {person}.") case _: print("I didn't quite understand that!")
Disciples of the functional programming school will surely love it; and seasoned developers who has had to tangle with the umpteenth business rules engine can look forward to some reprieve also. But what about day-to-day use cases? What makes Structural Pattern Matching useful for your typical Python project? What is it even, and why would you want to adopt it when you can solve complex problems without it?
The general concept – and I’ll walk you through how it all works soon enough – goes to the very heart of Computer Science and (especially) functional programming. Permeating all these different languages and their own take on this feature is a common vocabulary and understanding about what Pattern Matching is and the problems it tries to solve. Once you grasp the gist of pattern matching in Python you will recognize – and know how to apply – the concepts anywhere.
Tantalizingly I left a snippet of code heralding the new feature above. It doesn’t look too bad, right? it’s a function that tries to intelligently format a greeting:
>>> greet_person({"greeting": "Say my name"}) I didn't quite catch your name? >>> greet_person("Walter") Nice to meet you, Walter. >>> greet_person({"greeting": "Howdy", "name": "Cosmo"}) Howdy, Cosmo
But there’s nothing in greet_person
that you couldn’t do with a series of if
statements. And that, right there, is the crux of what pattern matching tries to do: remove the verbiage and tedium of if
statements and “getters” that interrogate the structure of an object to extract the information you want. In greet_person
I want – ideally – several pieces of information: a greeting
and a name
, and with graceful handling in case some or all of them is missing.
Manipulating data structures is a core part of programming, and the pattern matching system is there to help you achieve that. When you use if
statements, isinstance
calls, Exceptions and membership tests against objects, dictionaries, lists, tuples and sets you do so to ensure the structure of the data matches one or more patterns. That is what an ad hoc pattern matching engine looks like.
Consider what the match
code above looks like the old-fashioned way:
def greet_person_alt(p): msg = "I didn't quite understand that!" if isinstance(p, dict): if 'greeting' in p: greeting = p['greeting'] if 'name' in p: name = p['name'] msg = f"{greeting}, {name}" else: # ... etc ... else: # ... etc ... else: # ... etc ... print(msg)
This is just a part of the whole ordeal, and I made no effort to get clever either. But as you can see, deeply nested if
statements make it easy to miss a business rule or put it in the wrong place; even worse, you have to parse the whole structure to figure out the right place to make changes. Not to mention the size of it. Add just a few more rules or complex checks to determine the right greeting format and you would have to create your own home brew matching engine — this approach simply does not scale.
And that, then, brings us to the heart of Structural Pattern Matching: the match
and case
keywords. This is a problem that you have – and will have – in every facet of programming:
-
Do you have an inordinately deep and nested dict-of-dicts where you must check for the presence of keys and their values? You could use the structural pattern matcher.
-
Do you have complex business rules that depend on certain attributes in custom objects, like a
Customer
orSales
object? You could use the structural pattern matcher. -
Do you have to parse the output of files or streams of data from other systems? Maybe transform them from a list of primitives (strings, integers, etc.) into a
namedtuple
, dictionary or custom dataclass object? You could use the structural pattern matcher.
So let’s take a look at how it really works.
Read More ->
from Planet Python
via read more
No comments:
Post a Comment