Structural Pattern Matching in Python I

In this blog, which is a first of a three-part series, we talk about the structural pattern-matching support in Python.

GraphQL has a role beyond API Query Language- being the backbone of application Integration
background Coditation

Structural Pattern Matching in Python I

Python 3.10 introduced structural pattern-matching syntax in python. Typically used in functional programming languages such as Haskell, rust and hybrid programming languages such as scala, more and more object-oriented programming languages are adopting spm syntax as a core feature.

We start with basic definitions, types of pattern-matching, and basic pattern matching examples in this blog & move to pattern matching examples in the second part, and finally discuss class pattern in the third part.

What is structural pattern matching?

Structural pattern matching is a feature in some programming languages that allows you to compare objects based on their structure rather than just their type. This can be useful when you want to compare objects that may have different types but have the same underlying structure.
The specific syntax for structural pattern matching varies depending on the programming language. In some languages, it is implemented using a keyword such as match or a case, while in others it is implemented using the instanceof operator in combination with special syntax for specifying patterns.
Structural pattern matching can make your code more expressive and easier to read, as it allows you to directly compare the structure of objects without having to write long and complex type-checking code. It is available in languages such as Scala, Rust, Java (as of version 14), Python (as of version 3.10)
In python, a pattern is a value defined after the case keyword which acts as a minimal structural constraint against which a subject is matched.
In some cases, it also helps us bind the subject partly or fully to a free variable.

What are the different types of pattern matching in python?

There are 9 different types of patterns

  • Literal Pattern
  • Capture Pattern
  • Wildcard Pattern
  • AS Pattern
  • OR Pattern
  • Guard Pattern
  • Value Pattern
  • Sequence Pattern
  • Mapping Pattern
  • Class Pattern

Literal pattern matching

This includes matching literals in python including number, string, None, True, False, etc.
Let us understand with an example

def literal_patterns(subject):

    match subject:
        case 1:
            print("The subject matches with 1")
        case 1.0:
            print("The subject matches with 1.0")
        case 2+3j:
            print(f"The real and imaginary parts are 2 and 3 respectively.")
        case "Masashi Kishimoto":
            print(f"Masashi Kishimoto is the author of manga series which was first published in 1999.")
        case None:
            print("The subject matches with None")
        case True:
            print("The subject matches with True")

Here is the output in different cases of subject

    subject = 1 # Output: The subject matches with 1
    subject = 1.0 # Output: The subject matches with 1
    subject = 'Masashi Kishimoto' # Output: Masashi Kishimoto is the author of manga series which was first published in 1999.
    subject = None # Output: The subject matches with None
    subject = True # Output: The subject matches with 1
    subject = 2+3j # Output: The real and imaginary parts are 2 and 3 respectively.

In the above example, the case for 1.0 or True will not be called even if the value for a subject is 1.0 or True respectively.
Instead, the case for 1 will be used, As for literal pattern matching == or __eq__ is used.

Similarly, 0 will be a match for the subject value False.
F-strings are not allowed to be used as patterns or cases.

Capture pattern matching

These are patterns where we can assign a subject(partially or fully) to a free variable.

def capture_patterns(subject):

    match subject:
        case [a,]:
            print(f"Collection has only one entry: {a}")
        case (a, b):
            print(f"Find flowrate for volume {a}litres and time {b}secs")
        case value:
            print(f"{value} is the value of subject")

Here are the different types of outputs

    subject = (22,) # Output: Collection has only one entry: 22
    subject = [33, 44] # Output: Find flowrate for volume 33litres and time 44secs
    subject = 87 # Output: 87 is the value of the subject

  1. A free variable can be used only once for a single pattern - In the above example a can be seen in two patterns but its usage in each pattern is only once.
  2. An Irrefutable pattern in the above example value can only be used as the last pattern to be matched.
  3. The free variables a or b can be used outside the match scope while using an Irrefutable pattern value will result in UnboundLocalError: local variable 'value' referenced before assignment error
  4. Unless a class pattern is explicitly mentioned the list and tuple can be matched Ex: subject case match (22,66) [a,b] matched [22,66] (a,b) matched (22,) [a,] matched [22,] (a,) matched

Wildcard patterns

These patterns use single underscore _ to match a part of or whole subject.

def wildcard_patterns(subject):

    match subject:
        case (1,_,1):
            print(f"{subject} is a palindrome collection")
        case (_,_):
            print(f"{subject} is a collection of 2")
        case _:
            print("Unknown Match")

Here are the different outputs

    subject = (1,2) # Output: (1,2) is a collection of 2
    subject = (1,2,1) # Output: (1,2,1) is a palindrome collection
    subject = (1,2,3) # Output: Unknown Match

The wildcard pattern can be used when part of the subject matched to the wildcard is not explicitly used as in the case of the capture pattern.

AS patterns

This pattern is used to bind the free variable(name) on the right side of as to the subject matched with the pattern on its left side.

def as_patterns(subject):

    match subject:
        case "Kg" as mass:
            print(f"SI unit for mass is {mass}")
        case "N" as a force:
            print(f"SI unit for force is {force}")

Here are the different outputs

    subject = "Kg" # Output: SI unit for mass is Kg
    subject = "N" # Output: SI unit for force is N

The _(wildcard) cannot be used on the right side of as, as it can’t be reused. In case it’s used on the right side of as it will result in a syntax error. (SyntaxError: cannot use _ as a target).

OR patterns

This pattern works similarly to a or keyword but uses pipe | to do so.

def or_patterns(subject):

    match subject:
        case "RED" | "YELLOW" | "BLUE" as p_colour:
            print(f"{p_colour} is one of the primary colours") 
        case colour:
            print(f"{colour} is not one of the primary colours")

Here are the different outputs

    subject = "RED" # Output: RED is one of the primary colours
    subject = "GREEN" # Output: GREEN is not one of the primary colours


A guard is a if condition on top of patterns.

def guard(subject):

    match subject:
        case int(number) if number % 2:
            print(f"{number} is an odd integer")
        case int(number):
            print(f"{number} is an even integer")  
        case element:
            print(f"{element} is not an integer")

Here are the different outputs

    subject = 33 # Output: 33 is an odd integer
    subject = 34 # Output: 34 is an even integer

Its important to know that a guard condition is only checked if an initial pattern is matched.

In this part one of three parts series of structural pattern matching in python, we discussed the definition and basic pattern matching in python 3.10.
In the next blog, we will discuss Value Patterns, Sequence Patterns, and Mapping Patterns.

Hi, I am Harris Hujare. As a software developer, I design and implement scalable and maintainable systems. I have a passion for learning new technologies and creating software utilities making lives easier.

Want to receive update about our upcoming podcast?

Thanks for joining our newsletter.
Oops! Something went wrong.