Python 3.10 Structural Pattern Matching
In this post we will install the beta version of Python 3.10 and use the soon to be release Python Structural Pattern Matching. This is very similar to switch statements in C++. Microsoft outlines the C++ switch statement here: (LINK). This is a very exciting new feature of Python!
Demonstrating PEP 634: Structural Pattern Matching
Structural pattern matching has been added in the form of a match statement and case statements of patterns with associated actions. Patterns consist of sequences, mappings, primitive data types as well as class instances. Pattern matching enables programs to extract information from complex data types, branch on the structure of data, and apply specific actions based on different forms of data.
At the time of writing this post Python 3.9 is the latest version of Python and Python 3.10 is in beta mode. To be able to experiment with Python 3.10 we have to install the beta version, which requires a more elaborate approach than a normal Python installation.
How to Install a Beta Version of Python
Python 3.10.0b2 - Released May 31, 2021
Overview of Procedure
- Ubuntu Server 20.04 LTS
- Install required libraries
$ sudo apt-get install libssl-dev openssl make gcc
- Copy url for specific Python version desired
https://www.python.org/ftp/python/3.10.0/Python-3.10.0b2.tgz
- Go to /opt directory and download Python to here
$ cd /opt
$ wget https://www.python.org/ftp/python/3.10.0/Python-3.10.0b2.tgz
- Extract the archive
$ tar xzvf Python-3.10.0b2.tgz
- Drop into the new Python directory
$ cd Python-3.10.0b2
- Compile the new version - each of these take awhile
$ ./Configure
$ make
$ sudo install
- Make this version of Python usable anywhere
sudo ln -fs /opt/Python-3.10.0b2/Python /usr/bin/python3.10
- Check your Python Version now
$ python3 --version
success looks like:
Python 3.10.0b2
Using Match in Python - Examples
Here we run through some examples outlined in PEP 634 but I try to include the full code so that one can run the examples themselves.
Basic Use Case
Here we will create a function that gets a http error status code and uses the match to return the appropriate response.
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the Internet"
def main():
statuses = [400,400,404,400,418,404,418,'']
for status in statuses:
print(http_error(status))
if __name__ == "__main__":
main()
For the above code the statuses
list of integers and a string is looped through and each one sent to the http_error()
function where the response is returned to main()
and print to the terminal. The outputs are shown below.
OUTPUT:
Bad request
Bad request
Not found
Bad request
I'm a teapot
Not found
I'm a teapot
Something's wrong with the Internet
Note the last block: the variable name _
acts as a wildcard and insures the subject will always match. The use of _
is optional.
No Wildcard
Rerunning the code above but without the _
wildcard.
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
def main():
statuses = [400,400,404,400,418,404,418,'']
for status in statuses:
print(http_error(status))
if __name__ == "__main__":
main()
For the code above without the wild card the output is below. Take note of the last line. None
where before it was Something's wrong with the Internet
.
OUTPUT:
Bad request
Bad request
Not found
Bad request
I'm a teapot
Not found
I'm a teapot
None
A handy approach to remember depending on what you are trying to code.
Guard
We can add an if clause to a pattern, known as a “guard”. If the guard is false, match goes on to try the next case block. Note that value capture happens before the guard is evaluated:
def use_a_guard(x,y):
match x,y:
case x, y if x == y:
print(f"The point is located on the diagonal Y=X at {x}.")
case x, y:
print(f"Point is not on the diagonal.")
def main():
# USING GUARD
points = [[1,1], [1,2], [3,3]]
for point in points:
x = point[0]
y = point[1]
print(use_a_guard(x,y))
if __name__ == "__main__":
main()
The main()
function loops through the list points
which contains two element lists which represent x,y points. The use_a_guard
function checks to see if the points are on the diagonal, which means they equal each other. If x
and y
are the same values it returns a string stating such. This uses an if
statement. If x
and y
are not equal the if
statement is false and the match defaults to case x, y
. The outputs can be seen below.
The point is located on the diagonal Y=X at 1.
None
Point is not on the diagonal.
None
The point is located on the diagonal Y=X at 3.
None
Conclusions
A switch statement is usually more efficient than a set of nested ifs. Deciding whether to use if-then-else statements or a switch statement is based on readability and the expression that the statement is testing. The Python Structural Pattern Matching offers advanced features that allow classes, guards and more which make it even more useful.