Introduction
The Python match
–case
statement conditionally executes code depending on the value of an expression. It is meant to simplify if
–elif
–else
chains where the conditions are comparisons of the same expression to different values.
First introduced in Python version 3.10, the match
–case
syntax is a relatively late addition to the language. However, since its functionality can be completely implemented with if
–elif
–else
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 match
–case
is to look at the type of if
–elif
–else
it is meant to simplify. (If you are not familiar with Python’s if
–elif
–else
syntax, I recommend you first read the lesson about it here.)
The following example uses an if
–elif
–else
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 if
–elif
–else
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 match
–case
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 if
–elif
–else
with a match
–case
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 case
–match
in this example simplifies the if
–elif
–else
in the previous one because it eliminates the repeated flavor ==
code in the conditions.
Basic Python match-case Syntax
The match
–case
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.pattern1
…paternN
: A pattern to match against the expression. Patterns can be a literal value, the wildcard_
, or a variable with anif
, which will be discussed in the next section.
The Python match
–case
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 match
–case
runs.
Using a Variable if Pattern in the case Clause
The Python match
–case
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 match
–case
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 ofexpression
. It can have any valid name and is used withincondition
.condition
: The condition that determines whether there is a pattern match for thecase
. It should involve the variablevar
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 match
–case
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 match
–case
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 match
–case
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 match
–case
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 match
–case
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 match
–case
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 thecase
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 match
–case
statement conditionally executes code depending on the value of an expression. Introduced in Python 3.10, this syntax simplifies if
–elif
–else
chains when the comparisons involve the same expression that is repeated throughout.
The match
–case
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 match
–case
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)