The Python match-case Statement

Introduction

The Python matchcase statement conditionally executes code depending on the value of an expression. It is meant to simplify ifelifelse chains where the conditions are comparisons of the same expression to different values.

First introduced in Python version 3.10, the matchcase syntax is a relatively late addition to the language. However, since its functionality can be completely implemented with ifelifelse statements, it is not critical to earlier versions. Nonetheless, it is nice-to-have syntax that exists in many other programming languages.

match-case vs. if-elif-else

The best way to understand matchcase is to look at the type of ifelifelse it is meant to simplify. (If you are not familiar with Python’s ifelifelse syntax, I recommend you first read the lesson about it here.)

The following example uses an ifelifelse to print out reactions to an ice cream flavor.

Example:

flavor = "chocolate"

if flavor == "vanilla":
    print("Yum! Classic choice.")
elif flavor == "chocolate":
    print("Rich and indulgent!")
elif flavor == "strawberry":
    print("Sweet and fruity.")
elif flavor == "mint chocolate chip":
    print("Refreshing with a hint of chocolate.")
elif flavor == "cookie dough":
    print("Delicious chunks of cookie dough!")
else:
    print("Hmm, I'm not sure about that flavor.")

Output:

Rich and indulgent!

The example above uses an ifelifelse statement chain to print out different reaction messages to various ice cream flavors, as given by the flavor variable. In this specific instance, the flavor is set to “chocolate” on line 1, and therefore, the condition checked on line 5 is True, and the program prints the message “Rich and indulgent!”. Ice cream flavors that the if and elif clauses don’t check for will fail all conditions, and cause the else clause to print, “Hmm, I’m not sure about that flavor.”

As you can see, all the conditions in the example compare the same expression, the variable flavor, to some value, i.e., a flavor, so the example can be rewritten with a matchcase statement. Let’s do that.

Example:

flavor = "chocolate"

match flavor:
    case "vanilla":
        print("Yum! Classic choice.")
    case "chocolate":
        print("Rich and indulgent!")
    case "strawberry":
        print("Sweet and fruity.")
    case "mint chocolate chip":
        print("Refreshing with a hint of chocolate.")
    case "cookie dough":
        print("Delicious chunks of cookie dough!")
    case _:
        print("Hmm, I'm not sure about that flavor.")

This example has the same output as the previous one. It replaces the ifelifelse with a matchcase statement. The match keyword on line 3 declares which expression to use in the comparisons, and each case statement supplies a value to compare it with. If the case value matches the actual value of the expression, the code block below the case is executed, and the rest are skipped.

The underscore _ in the last case on line 15 is a wildcard that makes its code block execute when none of the other values match.

The casematch in this example simplifies the ifelifelse in the previous one because it eliminates the repeated flavor == code in the conditions.

Basic Python match-case Syntax

The matchcase syntax in Python consists of the match keyword followed by an expression and a colon :. The expression can be a variable, function call, or any other valid expression. Under the match line, there is indented code with the case clauses. Each case has a pattern to match followed by a colon : and the corresponding code to execute underneath if the pattern matches the expression.

match expression:
    case pattern1:
        code-block1
    case pattern2:
        code-block2
    ...
    case patternN:
        code-blockN
  • expression: An expression to match against, which can be a variable, function call, or any other valid expression.
  • pattern1paternN: A pattern to match against the expression. Patterns can be a literal value, the wildcard _, or a variable with an if, which will be discussed in the next section.

The Python matchcase statement works by comparing the expression given by match to each of the patterns given in the case clauses. The code block below the first pattern that matches is executed, and the rest are ignored.

As discussed in the previous example, the underscore _ signifies a wildcard that matches any value. If used, it must be in the last case, and its code block executes when none of the other patterns match. Using the wildcard is optional, and if no pattern matches without it, no code block in the matchcase runs.

Using a Variable if Pattern in the case Clause

The Python matchcase supports conditions that are more complex than straight comparisons. Those can be written with a variable followed by an if and a condition. The variable can have any valid name and is limited in scope to the case clause it’s used in. This is best understood with an example.

Example:

num = 10

match num:
    case 0:
        print("Zero")
    case n if n > 0:
        print("Positive number")
    case n if n < 0:
        print("Negative number")

Output:

Positive number

In the example above, the matchcase statement checks whether the number given by the variable num is zero, positive, or negative and prints out its determination. The first case on line 4 is a straight comparison to the literal 0. The second and third cases, however, are more complicated because they require comparisons with ranges of numbers (either > 0 or < 0). Those comparisons are implemented using a variable, n, and an if followed by a condition. The condition determines whether there is a match. If it’s true, there is a match. The variable is assigned the value of the expression in the match clause, and then the if uses it to evaluate the condition.

Here is the syntax of the variable and if pattern:

match expression:
    ...
    case var if condition
    ...
  • var: The variable that is assigned the value of expression. It can have any valid name and is used within condition.
  • condition: The condition that determines whether there is a pattern match for the case. It should involve the variable var and can be an expression of any complexity.

Using | in a case Clause

In many programming languages, the pipe character | has the meaning of “or” in various contexts, and Python is no exception. For a Python match-case statement, | can be used within a case clause to add more values to match the pattern. For example, case "a" | "b" | "c": means match "a" or "b" or "c".

Revisiting the ice cream example, suppose you realize that the cookie dough flavor deserves the same “Rich and indulgent!” reaction as the chocolate. You can reflect this by rewriting the case "chocolate": as case "chocolate" | "cookie dough":.

Example:

flavor = "cookie dough"

match flavor:
    case "vanilla":
        txt = "Yum! Classic choice."
    case "chocolate" | "cookie dough":
        txt = "Rich and indulgent!"
    case "strawberry":
        txt = "Sweet and fruity."
    case "mint chocolate chip":
        txt = "Refreshing with a hint of chocolate."
    case _:
        txt = "Hmm, I'm not sure about that flavor."

print(txt)

Output:

Rich and indulgent!

The example above removes the separate case for the cookie dough ice cream flavor and adds | "cookie dough" to the one for chocolate on line 6. Now, in case of “chocolate” or “cookie dough”, the program prints the same “Rich and indulgent!” message.

Using Dictionary Lookup as an Alternative to the match-case

The last matchcase code examples were designed to be simple. Their case code blocks are nothing more than print statements with various text. Nothing changes across the code blocks except for a single value, the text. The print function call stays the same, and therefore, we can take it out of the matchcase altogether and just use its code blocks to assign the text. Let’s see how by modifying the first ice cream example.

Example:

flavor = "chocolate"

match flavor:
    case "vanilla":
        txt = "Yum! Classic choice."
    case "chocolate":
        txt = "Rich and indulgent!"
    case "strawberry":
        txt = "Sweet and fruity."
    case "mint chocolate chip":
        txt = "Refreshing with a hint of chocolate."
    case "cookie dough":
        txt = "Delicious chunks of cookie dough!"
    case _:
        txt = "Hmm, I'm not sure about that flavor."

print(txt)

The example above is a refactoring of the original ice cream example. The difference is that now, instead of printing, each case code block assigns the text to be printed to the variable txt. The printing is done at the end on line 17.

A refactoring is a modification of code that otherwise leaves its functionality unchanged.

After the refactoring, it becomes apparent that the matchcase statement is nothing more than mappings from ice cream flavors (given in the case clauses) to reaction message texts (as assigned to txt). Consequently, that mapping can be represented by a dictionary data structure, which is a mapping from keys to values, where the keys are the flavors and the values are the messages.

Let’s refactor again to use a dictionary instead of a matchcase statement.

Example:

flavor_reactions = {
    "vanilla": "Yum! Classic choice.",
    "chocolate": "Rich and indulgent!",
    "strawberry": "Sweet and fruity.",
    "mint chocolate chip": "Refreshing with a hint of chocolate.",
    "cookie dough": "Delicious chunks of cookie dough!"
}

default_message = "Hmm, I'm not sure about that flavor."

flavor = "chocolate"
txt = flavor_reactions.get(flavor, default_message)
print(txt)

In the example above, the code is refactored to use a dictionary instead of a matchcase statement. The dictionary is defined beginning on line 1 to hold the mapping from ice cream flavors to reaction messages. The variable default_message is assigned the default message from the case _: clause. The code looks up the message that goes with the ice cream flavor on line 12. It uses the dictionary get() method. get() receives the key, which is flavor, as well as the default default_messages, which it returns when the key is not found in the dictionary.

Consider using dictionary lookup instead of a matchcase statement when you have all the following conditions:

  • All the case patterns are simple literal comparisons.
  • All the case clause bodies can be reduced to an assignment of a value.
  • There isn’t a lot of repetition in the values, i.e., you don’t use | in the case clauses.

When dictionaries can be used for that purpose, they have these advantages:

  • More succinct and less repetitive code.
  • Logic that can easily be changed on the fly by modifying the dictionary.
  • Logic that can easily be passed around with variables and function arguments.

Summary & Reference for the Python match-case Statement

The Python matchcase statement conditionally executes code depending on the value of an expression. Introduced in Python 3.10, this syntax simplifies ifelifelse chains when the comparisons involve the same expression that is repeated throughout.


The matchcase syntax in Python consists of the match keyword followed by an expression and a colon :. Under the match line, there is indented code with the case clauses. Each case has a pattern to match followed by a colon : and the corresponding code to execute underneath.

match expression:
    case pattern1:
        code-block1
    case pattern2:
        code-block2
    ...
    case patternN:
        code-blockN

The pattern can be a literal, the wildcard _, or a variable with an if.


The wildcard _ matches any value. It is optional, but when used, it must be in the last case, and its code block executes when none of the other patterns match.


More complex matching can be implemented with a pattern that consists of a variable name followed by the if keyword and a condition that involves the variable. Python assigns the value of the match expression to the variable, and the case matches if the condition is true.

match num:
    case 0:
        print("Zero")
    case n if n > 0:
        print("Positive number")
    case n if n < 0:
        print("Negative number")

The pipe character | can be used within a case clause to add more values to match the pattern.

match letter:
    case "a" | "b" | "c":
        print("One of the first three letters of the alphabet.")
    ...

Consider using dictionary lookup instead of a matchcase statement when all the case patterns are simple literal comparisons, all the case clause bodies can be reduced to an assignment of a value, and there isn’t a lot of repetition in the values, i.e., you don’t use | in the case clauses.

flavor_reactions = {
    "vanilla": "Yum! Classic choice.",
    "chocolate": "Rich and indulgent!",
    "strawberry": "Sweet and fruity.",
    "mint chocolate chip": "Refreshing with a hint of chocolate.",
    "cookie dough": "Delicious chunks of cookie dough!"
}

default_message = "Hmm, I'm not sure about that flavor."

flavor = "chocolate"
txt = flavor_reactions.get(flavor, default_message)
print(txt)