A little while ago, I had the opportunity to speak at the local Chennai Python user group about how to use the new match statement in python to write much better code. We discuss what the match statement is, how it differs from the switch-case statement in other languages, and how understanding that difference allows us to take advantage of the full power of the statement. We also take a look at some common use cases where the match statement shines.

If you have never used match before, this presentation will take you from zero to hero.

Here are the slides from the talk, along with some commentary. if you are reading this in email, make sure you enable the "download pictures" setting to see the slides. Note that the slides are for subscribers only, but signing up as a subscriber is free. So sign up 😊

The match statement made it's way to python in version 3.10. The funny thing is, the first time I encountered this statement, it seemed very complicated, and I was turned off from using it. It turned out that I had some fundamental misunderstanding in how to use it effectively. Once I understood what it is actually meant to do, then I started loving it. After f-strings, it is my favourite new feature in python.

Here is an example of the match statement in action. You write match <expression> and follow it up with different case statements. Python will evaluate the expression and then execute the code block corresponding to the result of the expression.

On first glance this looks similar to the switch/case statement from other languages like C++ or Java. And if you search around in Google, you will see many tutorials comparing it to exactly that. And this was the mistake that I made as well. I thought to myself, what is the big deal if all I am doing is replacing a simple if/else check. What is the need to add a new language feature just for this?

Isn't it interesing though, that the feature is called "Structural Pattern Matching" instead of "switch-case"? What exactly does that mean? In fact, that was the clue that I was on the wrong path in understanding the match statement.

Before going into Structuaral Pattern Matching, let us take a look at one more example of the match statement in action.

The function above makes a call to the Github API. If the call succeeds, then it returns the tuple ('OK', <user-id>). If it fails, then the return value is 'Error' along with the status code. Notice how the tuple has a different structure in both cases. Depending on the value in the first field, the data in the second field is different.

If I call this function and want to process the response, then I need to first check the first field. Then depending on that result, we need to do different things with the data in the second field. Now lets take a look at how we would do it with the match statement.

The case statement is a little more complex this time. Here it will match the first case statement of the value is a tuple, and the first field contains 'OK'. And python will match the second statement if the tuple has the first field value 'Error'.

But thats not all. If the case ('OK', id) matches, then Python will create a variable id and bind it to whatever was the second field value. We can then use that variable inside the code block.

As we can see here, the match statement does not just match the value of the expression. It matches whether the value has a certain structure. We give it a pattern - here we are saying, "match this case if the value matches a pattern of a tuple with the first field 'OK'".

This is very, very different from a switch-case statement from other languages you might know.

Now the name Structural Pattern Matching starts to make sense.

And not only does it check the pattern, but if the pattern matches, then you can specify variables on other parts of the pattern. The data in those parts of the pattern will be assigned to the specified variable.

Now that we have an idea for structural pattern matching, how do we use it effectively? We can't look at C++ or Java for inspiration because they don't have this feature. But it turns out there are many languages that do support structural pattern matching. These are the functional programming languages which have had this feature for long - and were the inspiration for Python to get this feature.

Above is a function for reversing a list in Haskell. On line 3 is the pattern match statement. Line 4 says if the structure is an empty list, then the return value is also an empty list. Line 5 says x:xs which means if it matches the pattern of having an item x followed by a list of items xs, then reverse the list xs and then append the first item x to the end.

Is Python's pattern matching sophisticated to do something like this? Take a look below

Apart from syntax, Python's code is line for line equivalent to the Haskell code. You can see that once more the case statements are matching the structure of the data: Either an empty list, or a list that contains first value (assigned to variable head) followed by remaining items rest.

So structural pattern matching can not only match patterns of the structure, but also extract parts of the pattern into variables - all in a single line of code.

Use Cases for the match statement

Now that we understand the feature, let us look at different use cases where it shines over the traditional if/else that we used to do in Python.

We have already seen the first example - when a function returns different structures. This kind of code was traditionally discouraged, because the caller needs to write a lot of ugly code to process the response.

But the match statement makes this easy.

Because match makes it easy to differentiate structural patterns, it opens up a new style of coding where we don't need to fear returning different structures for different cases.

Here is another example. This is a response returned from an API. As you can see, the response format differs based on a success or error. Returning responses with different structures is very common in many APIs. Traditionally, this was irritating to process as we had to manually check the structure and get the data we want.

match makes it easy.

In this example, we are matching a dictionary pattern. If the dict contains an errors key, then it will match the first case. If it has data which contains a hero which in turn has a name, then it will extract that name field and print out the output.

Above is another example of match. We have a Point data class which stores the x and y coordinates. We want to process certain patterns in a special way, example whether it is the origin point, or lies along one of the axis.

The match statement allows us to do this in very clean code - both matching the pattern and extracting certain parts of the pattern into variables.

🚨
Patterns are checked from top to bottom, so in this case the ordering of the case statements is important.

The match statement is also very handy when a variable could be of different types. In this case item could be a Book object or Music object. Note how we use _ to match a part of a pattern that we do not want to  use in the code block.

Having a variable be of multiple types is a practise that is strongle discouraged in object oriented programming, but is very common in both functional programming as well as dynamic languages like Python. So the match statement is very helpful in these situations.

One final example. In this code, the friends field could be None, or it could be a single Person object, or it could be a list of Person objects. The match statement allows us to handle all these cases easily. Note the as f syntax to bind the whole value to a variable.

Final Thoughts

One important thing to realise is that the case statement uses its own pattern syntax. It is not normal python code in there! This trips up a lot of people!!

For example, the code below does not behave as expected

This code will always enter the first case statement. The pattern for the first case statement is saying "match any value" and assign the variable NORTH to that value.

Here is the correct way to write that code

Similarly, if we have case Point(0, 0) then it does not mean to create a new object Point(0, 0). It means to match the pattern when the value is a Point object containing x=0 and y=0.

The code after the case is a pattern syntax and not the regular python code. Keep that in mind.

There are many more powerful use cases for match. The fact that it makes it easy to handle different structures also allows us to write code to take advantage of that.

This leads to a different style of coding altogether.

Did you like this article?

If you liked this article, consider subscribing to this site. Subscribing is free.

Why subscribe? Here are three reasons:

  1. You will get every new article as an email in your inbox, so you never miss an article
  2. You will be able to comment on all the posts, ask questions, etc
  3. Once in a while, I will be posting conference talk slides, longer form articles (such as this one), and other content as subscriber-only

Tagged in: