Python Match Case: What It Is & How To Use It

by | Python

Over the years, Python has consistently introduced new features to improve its ease of use for programmers.

One such feature, added in Python 3.10, is the match case statement. The match case statement brings a new level of convenience and readability to Python code.

The match statement allows you to perform pattern matching on values. It encourages more readable and expressive ways to destructure and check data by comparing a value against several possible patterns with the match statement.

Furthermore, it provides a clearer alternative to the deeply nested if-elif-else statement.

In this article, we’ll explore the Python Match Case statement. We’ll review the syntax, examples, and use cases of the match case statement to help you better understand the concepts.

Let’s get into it!

Python Match Case

What is the Syntax of Match Case in Python?

The basic syntax of the match case statement is given below:

match expression_to_match:
    case pattern1:
        # Code block for pattern1
    case pattern2:
        # Code block for pattern2
    ...
    case patternN:
        # Code block for patternN
    case _:
        # Catch-all, default code block (optional)

This code snippet evaluates the expression_to_match and compares it against several patterns (pattern1, pattern2, …, patternN).

When a match is found, the corresponding code block is executed.

If none of the patterns match, and there is a catch-all case ‘_‘ the default code block is executed. It’s a more expressive way to handle conditional logic, similar to a switch statement in other languages.

The following is a simple example of a match case to illustrate the syntax:

A simple example of match case

In this example:

  1. The match statement performs structural pattern matching by checking the value of x.
  2. Patterns are checked in sequence. Once a pattern matches, its corresponding code block is executed and no further patterns are checked.
  3. The use of str(y) and int(x) in the case patterns are type patterns that match values of specific types and bind them to the specified variable names.

The match case statement is similar to the switch statement in other languages like Java and C++.

4 Pattern Varieties in Match Case in Python

You can use case matches in a range of applications, which is why it has a number of pattern varieties.

In this section, we’ll show you its 4 pattern varieties. Specifically, we’ll go over the following:

  1. Destructuring with Patterns
  2. Literal and Name Patterns
  3. Wildcard and OR Patterns
  4. Type Patterns and Guards
4 Pattern Varieties in Match Case

1. What is Destructuring With Patterns?

Destructuring with patterns refers to breaking down a data structure into its constituent parts and matching those parts against certain patterns.

It’s a way of accessing the internals of a data structure (like tuples, lists, or dictionaries) within the pattern-matching process.

Suppose we have a list of 2D points represented as tuples. We want to identify points that lie on the X-axis or Y-axis.

The following code demonstrates the above scenario:

Destructuring with patterns with multiple values

In this example:

  1. The tuple (0, y) destructures the point, matching any tuple where the first element is 0 (indicating it’s on the Y-axis) and binds the second element to y. The match keyword initiates the match case statement.
  2. Similarly, the tuple (x, 0) matches any point on the X-axis and binds the x-coordinate to x.

2. What Are Literal And Name Patterns?

Literal patterns match specific constant values. Common literal patterns include numbers, strings, True, False, None, and even enum values.

On the other hand, name patterns bind the matched value to a variable. If you use a name pattern, whatever value the match statement is evaluating against will be bound to that name for the duration of the corresponding case block.

Consider a function that interprets different command strings and provides responses:

Example of literal and name patterns to match status

In the above code:

  1. “start” and “stop” are literal patterns. They match the exact string values “start” and “stop”, respectively.
  2. cmd is a name pattern. If the cmd value does not match any of the prior patterns, it gets bound to cmd, and its value is then used within the string formatting in the return statement.

3. What Are Wildcard And OR Patterns?

Wildcard patterns are represented by the underscore (_). The wildcard pattern matches any value but does not bind it to a name. It’s often used as a catch-all pattern when no other patterns match.

OR Patterns allows you to combine multiple patterns using the | operator. The associated code block is executed if any of the combined patterns match.

Suppose we have a function that categorizes numbers based on certain properties. Consider the following code:

Example of wildcard and or matched pattern with optional arguments

In this example:

  1. 0 is a literal pattern.
  2. 1 | 2 | 3 is an OR pattern that matches if n is either 1, 2, or 3.
  3. _ is a wildcard pattern that matches any value of n not already matched by previous patterns.

4. What Are Type Patterns and Guards?

Type patterns allow matching values based on their type. You can also destructure and bind the value during the match if it matches the expected type.

Guards are conditional expressions you can add to a case to refine the pattern matching further.

The corresponding case block will only execute if the pattern matches and the guard condition evaluates to True.

They are helpful when dealing with more complicated pattern matching.

Let’s say you’re writing a function to describe different kinds of inputs. Consider the following switch statements:

Basic example of type patterns and guards using the case keyword

In this example:

  1. int(x) if x > 0 is a type pattern combined with a guard. The value is matched if it’s of type int and if it’s greater than 0.
  2. str(s) if len(s) < 5 matches values of type str with a length less than 5.

You can call the function with various inputs as shown below:

print(describe_input(10))       # Should print: "Positive integer: 10"
print(describe_input(-3))       # Should print: "Non-positive integer: -3"
print(describe_input("hello"))  # Should print: "String: hello"

4 Applications of Match Case in Python

In the above sections, we learned the syntax and pattern varieties of match case. Let’s go ahead and put what we’ve learned so far into practice.

Read on, as we have a look at 4 applications of match case.

Specifically, we will go over the following:

  1. Data Structure Parsing
  2. State Machines
  3. Type Dispatching
  4. Error Handling and Validation
4 Applications of Match Case

1. Data Structure Parsing

When working with complex data structures, especially nested ones, the match case simplifies the extraction and validation process.

For instance, when parsing abstract syntax trees (ASTs) or JSON-like structures, pattern matching can make the code significantly more readable by reducing the nested conditional checks.

Consider the following example where we match specific shapes in a list of geometric descriptors:

Example of data structure parsing

The above example iterates over a list of shapes, each described by a dictionary.

Using pattern matching, it identifies circles and rectangles based on their “type” and relevant dimensions.

Upon identification, it prints out the shape’s specifics: either the circle’s radius or the rectangle’s width and height.

You can use if-else statements to perform the above operations as well. However, with an if statement, you’ll have to write a deeply nested code block which is not recommended.

2. State Machines

In applications where an entity can be in multiple states (e.g., a game character or a network protocol), the match case offers a clear way to handle state transitions and associated actions.

Consider the following example where we handle network packet types in a simplified protocol:

Example of state machines

The code evaluates a packet object using Python‘s pattern sequence matching.

Depending on the type and structure of the packet — whether it’s a Connect, Disconnect, or Message — the code extracts relevant data, like the username or message content, and then invokes the appropriate handler function (handle_connect, handle_disconnect, or process_message) with the extracted data.

3. Type Dispatching

The match statement can replace convoluted type-checking logic when behavior varies based on input types. It offers a cleaner alternative to methods like isinstance().

Consider the following example where we process different input types in a generic function:

Example of type dispacting in case branch

The process function uses Python’s pattern matching to determine the type of its input data.

Depending on whether the data is an integer, string, or list, it delegates processing to the respective handle_integer, handle_string, or handle_list functions, passing the actual value to the chosen handler.

4. Error Handling And Validation

For functions expecting specific formats or values, pattern matching can be used for validation, returning errors, or handling exceptions when data doesn’t match expected patterns.

Consider the following example of validating API responses:

# 1. Define the handler functions:
def process_data(content):
    print(f"Processing data: {content}")

def log_error(msg):
    print(f"Error encountered: {msg}")

# 2. Create a sample api_response. You can change this to test different scenarios:
api_response = {
    "status": "success",
    "data": {"content": "Sample API data"}
}

# Provided code:
match api_response:
    case {"status": "success", "data": dict(content)}:
        process_data(content)
    case {"status": "error", "message": str(msg)}:
        log_error(msg)

The code inspects an api_response, which is expected to be a dictionary, using Python’s pattern matching.

If the response contains a status of “success” along with associated data, the process_data function is called with that data.

On the other hand, if the status is “error” and comes with an error message, the log_error function is invoked with the provided error message.

Example of Error Handling And Validation

Supercharge your Python journey with Code Interpreter by watching the following video:

Final Thoughts

As you write complex applications, the need for concise, readable code becomes more important. The match case is one tool that allows you to categorize and handle different data structures or conditions effortlessly.

Learning match case guarantees that your code is more maintainable and intuitive. It allows you to compress extensive logic into a few lines of code.

There are multiple use cases for this statement, and over time, as you work on more extensive projects, you’ll often use it to write clean and efficient code.

Frequently Asked Questions

In this section, you will find some frequently asked questions you may have when working with match case.

Male programmer writing Python code

What is the alternative to match case in Python?

Before the introduction of the match case statement in Python 3.10, developers commonly used if statements or if-elif-else statements or dictionaries to implement switch-like structures.

These alternatives can still be used in older Python versions or if you prefer not to use the match case statement for a specific scenario.

How can I use regex for case matching in Python?

To use regular expressions for case matching in a Python match case statement, import the re module and use re.match(pattern, string) in the case conditions.

The re.match function returns a match object if the pattern is found in the input string.

import re

input_string = "some text"
match input_string:
    case re.match(r"\d+", input_string):
        # Matched a number
    case re.match(r"[a-zA-Z]+", input_string):
        # Matched a word
    case _:
        # Default case

How do I apply fallthrough in Python match case statement?

Python does not allow explicit fallthrough functionality in match case statements like some other languages’ switch-case constructs.

Instead, you can use a sequence pattern to test multiple cases in a single case statement:

match value:
    case "a" | "b" | "c":
        # Code for handling cases a, b, and c

Can I match case based on data type in Python?

Yes, you can use the match case statement to match based on the data type of a value:

value = 5
match value:
    case int():
        # Matched integer
    case str():
        # Matched string
    case _:
        # Default case

How to deal with Python match case statement with enums?

You can use the match case statement with Python Enums by matching the specific Enum value:

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

color = Color.RED
match color:
    case Color.RED:
        # Matched red
    case Color.GREEN:
        # Matched green
    case Color.BLUE:
        # Matched blue

Is there a way to use match case for substrings in Python?

Yes, you can use in for case matching with substrings in a Python match case statement:

input_string = "This is a test"
match input_string:
    case _ if "test" in input_string:
        # Matched substring 'test'
    case _:
        # Default case
author avatar
Sam McKay, CFA
Sam is Enterprise DNA's CEO & Founder. He helps individuals and organizations develop data driven cultures and create enterprise value by delivering business intelligence training and education.

Related Posts