Structural Pattern Matching
In the previous lesson, I showed you the improved error messages in Python 3.10. In this lesson, I’ll introduce you to the new pattern matching syntax. Python 3.10 has introduced two new keywords
case that, when combined, give you a new way of doing conditional code blocks.
This feature is intended to be used instead of certain kinds of long
if ... else blocks. If you’ve coded in a language that supports
switch statements, the base case of pattern matching will feel familiar.
This case has what is called a guard statement: the
if block just after the class on the same line. Line 11 translates into “The variable
c, is a
Card class, capital
C, that has an attribute called
.suit, whose value is either a heart or a diamond.” Line 13 matches any instance of the
Since line 11 is checked before line 13, this will be the clubs and spade cases. Line 15 is a default catchall, which should only trigger if the content of the
card variable being matched does not contain a
Card object. Let’s play with this code just a little bit.
The sum of nothing is zero. Line 6 is clever. It captures the first item in the list and then uses the star operator (
*) to capture the rest of the list. Line 7 then adds the value captured in
first to the result of a recursive call to
sum_list(), passing in as the argument to the recursive call the remaining part of the list. Let’s try this out.
Notice that nothing comes back. No
case pattern matched, so the function simply returned after the pattern matching statement. With no explicit return call, the function returned
None. With a bit of thought, a safer function can be created. Let’s give it a try.
The first improvement here is to add some type information to the pattern being matched on line 6. This says when
first is captured, it can be an integer or a float, and the remainder of the list goes in
rest like before.
If it is only a factor of
"buzz". Finally, if it is neither, print the number. There are many ways to implement this code, and that is one of the reasons it pops up in coding interviews.
This particular implementation is pretty straightforward. Two variables are created containing the number modded with
3 and the number modded with
5. A condition checks if the number’s a factor of both
5, then just the factor of
5, then the factor of
3, and then the default. Before transforming this into a
match ... case statement, let’s make sure it works.
Then, finally, the default condition. Honestly, this isn’t much different from the
if ... else block, but it does show how you can do pattern matching across multiple variables, creating some fairly complicated conditions.
08:36 As a final demonstration, let’s combine some of these concepts together. This is a sort function using pattern matching. It expects a list of items passed in and returns a sorted version of the list.
This pattern will match if the list is two items long and the first one is less than or equal to the second. The third pattern is also two items. As this pattern only fires if the one before failed,
y must be greater than
09:26 The next pattern is a three-item case. It has a guard for the situation where the items are already sorted. After that, the reverse is checked for. And finally, here’s the case that handles everything else.
This case captures the first item in the list into a variable named
p and puts the remainder of the list into
rest. Two new lists are then created, one with all the items less than or equal to
p, and the second with all the items greater than
p. Each of these lists are then passed recursively to
Finally, the sorted smaller items list,
p, in a list itself and the sorted larger items list are combined into the result. Hopefully, this gives you an idea of the power of the new
match ... case statements.
10:17 Patterns can contain multiple variables to capture, guards, and default values. In fact, that’s not all there is. There’s even still more. The pattern matching syntax supports some fairly complicated use cases.
Consider a mouse click event that takes a tuple as an argument, the point on the screen where the user clicked. The example case here captures the point tuple and assigns its pieces into
If your class being matched is a data class, then the order of items in the class specify how this matching happens. There’s also some meta information you can attach to a non data class to specify the order of matching like this, but I’ll leave that to you to dig out of the docs if you’re curious. Pattern statements also support the
as keyword similar to context managers. In this situation, it is storing the matched item into
k for use in the
11:49 The syntax of capturing a match means that you can’t pattern match against the contents of a variable. Any use of a variable will be treated as a capture target, overwriting the variable with the item being matched.
Take a look at this statement. The first case is treated like a literal because
person.full_name is using the dot notation. Even though
.full_name could be a variable, because it’s an attribute of
person, it’s treated as if it’s a literal for matching purposes. By contrast, the second case,
full_name, is on its own and therefore will be treated like a capture. If this case fires, the name variable being matched will overwrite whatever was in
Become a Member to join the conversation.