python-course.eu

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.

Patterns

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

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

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Upcoming online Courses

Python Basics for Beginners

17 Oct 2022 to 21 Oct 2022

Intensive Advanced Course

17 Oct 2022 to 21 Oct 2022

Python for Engineers and Scientists

17 Oct 2022 to 21 Oct 2022

Object Oriented Programming with Python

19 Oct 2022 to 21 Oct 2022

Enrol here