17. Structural Pattern Matching
By Bernd Klein. Last modified: 29 Jun 2022.
Introduction
'Structural Pattern Matching' was newly introduced in Python 3.10. The syntax for this new feature was proposed in PEP 622 in JUne 2020. The pattern matching statement of Python was inspired by similar syntax found in Scala, Erlang, and other languages. In its simplest form it behaves like the switch statement of C, C++, or Java. Yet, there are more improved use cases for this feaure, as you will learn in this tutorial. We will learn that it is possible in Python to unpack a pattern into its basic component.
Before you go on with this chapter, you have to make sure that Pyhton version 3.10 or higher is running on your system. The easiest way to find out is to use version from sys:
import sys
sys.version
OUTPUT:
'3.10.2 | packaged by conda-forge | (main, Feb 1 2022, 19:29:00) [GCC 9.4.0]'
Let us look at a simple structural pattern matching example. We write a function which reacts to a chosen language:
def greeting(language):
chosen_language = language.capitalize()
match chosen_language:
case 'English':
print('Hello')
case 'German':
print('Hallo!')
greeting('english')
OUTPUT:
Hello
As you most probably know, German is not only spoken in Germany, but in Austria and parts of Switzerland as well. This example shows now a usecase in which the structural pattern matching makes things easier:
def greeting(language):
chosen_language = language.split()
match chosen_language:
case ['English']:
print('Hello')
case ['German']:
print('Hallo!')
case ['German', 'AT']:
print(f'Servus!')
case ['German', 'CH']:
print(f'Grüezi!')
case ['German', 'DE']:
print(f'Hallo')
case ['German', 'LU']:
print(f'Gudden Dag')
case ['German', 'LI']:
print(f'Grüezi')
for lang in ['English', 'German LU', 'German CH']:
greeting(lang)
OUTPUT:
Hello Gudden Dag Grüezi!
Without match
the Python code would like similar to the following code:
def greeting(language):
chosen_language = language.split()
if len(chosen_language) == 1:
if chosen_language == ['English']:
print('Hello')
elif chosen_language == ['German']:
print('Hallo!')
elif len(chosen_language) == 2:
if chosen_language == ['German', 'AT']:
print(f'Servus!')
elif chosen_language == ['German', 'CH']:
print(f'Grüezi!')
elif chosen_language == ['German', 'DE']:
print(f'Hallo')
elif chosen_language == ['German', 'LU']:
print(f'Gudden Dag')
elif chosen_language == ['German', 'LI']:
print(f'Grüezi')
for lang in ['English', 'German LU', 'German CH']:
greeting(lang)
OUTPUT:
Hello Gudden Dag Grüezi!
We could improve the previous code a tiny little bit, but it should be clear that the match
is superior in its clarity in this case.
We can further improve our example: What if somebody choses an unknown language or an unknown language plus a locale? It is very easy. Instead of using a string literal in our pattern, we use variable names:
def greeting(language):
chosen_language = language.split()
match chosen_language:
case ['English']:
print('Hello')
case ['German']:
print('Hallo!')
case [unknown_language]:
print(f"So far, we don't know how to greet in {unknown_language}!")
case ['German', 'AT']:
print(f'Servus!')
case ['German', 'CH']:
print(f'Grüezi!')
case ['German', 'DE']:
print(f'Hallo')
case ['German', 'LU']:
print(f'Gudden Dag')
case ['German', 'LI']:
print(f'Grüezi')
case [lang, locale]:
print(f"Sorry, we don't know {lang} in the locale {locale}!")
for lang in ['English', 'German LU', 'French', 'German CH', 'English CA']:
greeting(lang)
OUTPUT:
Hello Gudden Dag So far, we don't know how to greet in French! Grüezi! Sorry, we don't know English in the locale CA!
We present an interesting use case in the form of an imaginary adventure game. Gamers could write actions as strings in the various formats, like
- help
- show inventory
- go north
- go south
- drop shield weapon
- drop all weapons
- and so on
command = 'go north'
command = 'drop ax shield'
match command.split():
case ["help"]:
print("""You can use the following commands:
""")
case ["go", direction]:
print(direction)
case ["drop", *objects]:
print(objects)
# Other cases
case _:
print(f"Sorry, I couldn't understand {command!r}")
OUTPUT:
['ax', 'shield']
def factorial(n):
match n:
case 0 | 1:
return 1
case _:
return n * factorial(n - 1)
for i in range(6):
print(i, factorial(i))
OUTPUT:
0 1 1 1 2 2 3 6 4 24 5 120
Live Python training
Enjoying this page? We offer live Python training courses covering the content of this site.
Upcoming online Courses