Pattern Matching in Python
By the Syncfusion DevTutorials team · Last updated
The match statement: cases, the | pattern for grouping values, case _ as the default, the bare-name trap, and when match beats an if chain.
Imagine sorting the post at a school office.
You pick up each envelope and look at it. A bill goes to accounts. A letter from a parent goes to the class teacher. A parcel goes to the store room. Anything else goes in the tray to deal with later.
You are not asking one yes-or-no question. You are looking at the shape of the thing in your hand and matching it to a case you already know.
Python can do the same with match.
Sorting one value into cases
command = "report"
match command:
case "report":
print("Printing the report card")
case "attendance":
print("Showing the attendance sheet")
case _:
print("Unknown command")
Python took the value of command and tried each case from the top. The first one that matched ran, and the rest were skipped.
What You Will Learn
In this lesson, you will learn:
- What pattern matching is, and what it is for
- How to write a
matchstatement and itscaseclauses - How to match plain values such as numbers and text
- How to let one
casecover several values with| - How to catch everything that did not match, using
case _ - When
matchreads better thanif elif else, and when it does not
match compares one value against several patterns and runs the first one that fits.
Pattern Matching
What Is Pattern Matching?
Pattern matching means comparing a value against several shapes, called patterns, and acting on the first one that fits.
A pattern is the value or shape a case is written to accept — 35, "report", or a pair like ("maths", 78) are all patterns.
It arrived in Python 3.10. Code written for older versions does not have it, so you will still meet plenty of if elif else chains doing the same job.
Why Pattern Matching Is Used
When a program has to react to one value that could be several different things, an if elif else chain repeats the variable name on every line.
The same name on every line
command = "attendance"
if command == "report":
print("Printing the report card")
elif command == "attendance":
print("Showing the attendance sheet")
elif command == "marks":
print("Showing the mark sheet")
else:
print("Unknown command")
command == appears three times. It is easy to misspell one, and easy to compare the wrong variable by mistake.
match names the value once, at the top, and then each case just states what it is looking for:
The value named once
command = "attendance"
match command:
case "report":
print("Printing the report card")
case "attendance":
print("Showing the attendance sheet")
case "marks":
print("Showing the mark sheet")
case _:
print("Unknown command")
Same result, and the shape of the code now says what it is doing: one value, several possible cases. Each case line reads like the answer it is waiting for.
Matching Variables
The value at the top of a match is called the subject. It can be a variable, or anything that produces a value.
The subject can be worked out on the spot
marks = 78
match marks // 10:
case 10:
print("Full marks")
case 9:
print("Ninety-something")
case 7:
print("Seventy-something")
case _:
print("Something else")
78 // 10 is 7, so the third case matched.
The match Statement
match subject:
case pattern:
code to run
case another pattern:
code to run
The rules are the same shape as an if:
- the
matchline ends in a colon - every
caseline ends in a colon caselines are indented inside thematch- the code for each case is indented again
A match with nothing in it
day = "Monday"
match day:
print("Weekday")
IndentationError — the match opened a block and nothing was inside it.
The case Clause
A case holds a pattern and a block of code. Python tries the cases from the top and stops at the first match.
Only the first match runs
grade = "A"
match grade:
case "A":
print("Excellent")
case "A":
print("This line can never run")
case _:
print("Something else")
The second case "A" is unreachable. Python matched the first one and stopped, exactly as an if elif chain does.
Basic Pattern Matching
The simplest pattern is a plain value to compare against.
Matching a number
lives = 2
match lives:
case 3:
print("Full lives")
case 2:
print("One lost")
case 1:
print("Last chance")
case 0:
print("Game over")
If several plain values should do the same thing, you could write each one as its own case. But there is a shorter way, which comes next.
OR Pattern
A single | lets one case match several values. Read it as "or".
One case, several values
day = "Sunday"
match day:
case "Saturday" | "Sunday":
print("Weekend")
case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
print("School day")
case _:
print("Not a day")
Two cases cover all seven days. The same thing as an if would need day == "Saturday" or day == "Sunday", with the variable named twice.
Grouping grades
grade = "C"
match grade:
case "A" | "B":
print("Above average")
case "C" | "D":
print("Average")
case "F":
print("Needs help")
case _:
print("Unknown grade")
In a case, | means "or". It is not the bitwise OR operator here, even though it is the same symbol.
Default Case (case _)
case _ matches anything. The _ is a single underscore, and it is Python's way of saying "I do not care what the value is".
It is the else of a match, and like else it goes last.
Catching everything else
command = "homework"
match command:
case "report":
print("Printing the report card")
case "attendance":
print("Showing the attendance sheet")
case _:
print("Unknown command:", command)
Without a case _, a value that matches nothing simply does nothing:
No default, no output
command = "homework"
match command:
case "report":
print("Printing the report card")
case "attendance":
print("Showing the attendance sheet")
print("Finished")
Nothing was printed about the command. No error, no warning. That silence is why a case _ is worth adding even when you believe every value is covered.
Putting case _ anywhere but last would make every case below it unreachable, and Python refuses outright:
A default in the wrong place
command = "report"
match command:
case _:
print("Unknown command")
case "report":
print("Printing the report card")
SyntaxError: wildcard makes remaining patterns unreachable. This is one of the few ordering mistakes a language will catch for you — the same mistake in an if elif else chain runs happily and gives the wrong answer.
Matching Literal Values
A literal is a value written out in full: 78, "report", True, None.
Different kinds of literal
answer = None
match answer:
case True:
print("Yes")
case False:
print("No")
case None:
print("Not answered yet")
case _:
print("Something unexpected")
One trap is worth knowing early. A bare name in a case is not compared against. It captures the value instead, like a new box that catches whatever the subject was.
Python refuses to compile that when other cases follow it:
A bare name with cases after it
PASS_MARK = 35
marks = 78
match marks:
case PASS_MARK:
print("Matched the pass mark")
case _:
print("Did not match")
SyntaxError: name capture 'PASS_MARK' makes remaining patterns unreachable — Python saw that the case would swallow every value, leaving the cases below it dead, and refused the file.
That is the helpful version. The dangerous one is a bare name in the last case, where nothing is left to become unreachable:
A bare name as the last case, silently matching
PASS_MARK = 35
marks = 78
match marks:
case 100:
print("Full marks")
case PASS_MARK:
print("Matched the pass mark:", PASS_MARK)
It printed Matched the pass mark: 78. The file compiled, nothing warned, and PASS_MARK is now 78 inside that block — the case caught the subject and rebound the name over it.
To compare against a value, write the value out:
Comparing against a value properly
marks = 78
match marks:
case 35:
print("Exactly the pass mark")
case _:
print("Not the pass mark")
A name reached through a dot is compared, because it cannot be a capture:
A dotted name is compared, not captured
import math
value = 3.141592653589793
match value:
case math.pi:
print("That is pi")
case _:
print("Not pi")
So a constant works in a case when it lives on something — math.pi, or your own settings object — and not when it is a plain name.
A bare name in a case captures the value rather than comparing with it. With cases after it, Python refuses to compile the file. As the last case it compiles, matches everything, and quietly rebinds the name. Write the literal value instead.
Matching Multiple Patterns
Patterns can also describe the shape of a value, not just its exact contents. A pair of values can be matched as a pair.
Matching a pair of values
result = ("maths", 78)
match result:
case ("maths", marks):
print("Maths marks:", marks)
case ("science", marks):
print("Science marks:", marks)
case _:
print("Unknown subject")
The first case matched because the subject is a pair whose first item is "maths". The second item was captured into marks, which the block then used.
This is where a bare name earns its keep: inside a larger pattern, it captures the part you want. A bare name is dangerous when it fills a whole case by itself, but useful inside a pair like ("maths", marks), where it catches only one piece.
Matching on how many items there are
for result in [("maths", 78), ("science", 85, "A"), ()]:
match result:
case ():
print("No result recorded")
case (subject, marks):
print(subject, "scored", marks)
case (subject, marks, grade):
print(subject, "scored", marks, "grade", grade)
Three different shapes, three different cases, and no counting or index checks anywhere.
A case can also carry an extra test, written with if. It is called a guard:
A case with an extra test
result = ("maths", 20)
match result:
case (subject, marks) if marks >= 35:
print(subject, "passed with", marks)
case (subject, marks):
print(subject, "failed with", marks)
The first case has a guard: it only runs when the shape matches and the guard is true. Here the shape matched, but marks >= 35 was false, so Python moved on to the second case.
Pattern Matching vs if-elif-else
Both do the same job. The difference is which one makes the code clearer.
Use match when | Use if elif else when |
|---|---|
| One value is compared against many fixed options | Each branch tests something different |
| The options are literal values such as commands or codes | The tests are ranges, like marks >= 80 |
| You are pulling pieces out of a pair or a list | There is only one condition |
Several values share one outcome, using | | A condition combines several variables |
Ranges are the clearest dividing line. A grade boundary is not a fixed value, so if elif else stays the right tool:
Ranges belong in an if chain
marks = 78
if marks >= 80:
print("Grade A")
elif marks >= 60:
print("Grade B")
elif marks >= 35:
print("Grade C")
else:
print("Fail")
Fixed options belong in a match:
Fixed options belong in a match
grade = "B"
match grade:
case "A" | "B":
print("Above average")
case "C" | "D":
print("Average")
case _:
print("Needs help")
Ask what the branches are testing. Many different questions means if elif else. One value against many fixed answers means match.
Common mistakes
Each row is one mistake. The middle column is what you will actually see on screen.
| Mistake | What you see | Why it happens | Fix |
|---|---|---|---|
| Using a bare name as a value to compare | SyntaxError: name capture ... makes remaining patterns unreachable | A bare name captures the value instead of comparing | Write the literal: case 35: |
| A bare name in the last case | It matches everything, with no error at all | Nothing follows it, so nothing becomes unreachable | Write the literal, or use case _ |
Putting case _ first | SyntaxError: wildcard makes remaining patterns unreachable | case _ matches anything, so nothing below it could be reached | Put case _ last |
Leaving out case _ | Nothing happens, and no error | A value that matches nothing simply falls through | Add case _ to catch the rest |
Forgetting a colon on match or case | SyntaxError: expected ':' | Both lines open a block | End both lines with : |
Not indenting the case lines | IndentationError: expected an indented block | case lines live inside the match block | Indent each case by four spaces |
Using || for "or" in a case | SyntaxError: invalid syntax | Python uses a single | in a pattern | case "A" | "B": |
| Expecting several cases to run | Only one message appears | match stops at the first case that fits | Combine the values with | |
Using match for ranges | Cases that never match | A pattern compares shapes and values, not ranges | Use if elif else, or add a guard with if |
| Repeating a case | A branch that silently never runs | An earlier identical case already matched | Remove the duplicate |
What to Remember
matchcompares one value against several patterns and runs the first one that fits.- The value at the top is the subject. It is named once, not on every branch.
- The
matchline and everycaseline end in a colon, and each block is indented. - Only one case ever runs, exactly as in an
if elif elsechain. - A single
|inside a pattern means "or", so one case can cover several values. case _matches anything. It is theelseof amatch, and it goes last.- Without
case _, an unmatched value does nothing at all — no output and no error. - A bare name in a
casecaptures the value instead of comparing with it. With cases after it Python refuses the file; as the last case it silently matches everything. Write the literal, or reach the constant through a dot. - A pattern can describe a shape, pulling pieces out of a pair or a list as it matches.
- A
casecan carry an extraiftest, called a guard. - Use
matchfor one value against fixed options, andif elif elsefor ranges and unrelated tests. matchneeds Python 3.10 or newer.
Next: Loops