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 match statement and its case clauses
  • How to match plain values such as numbers and text
  • How to let one case cover several values with |
  • How to catch everything that did not match, using case _
  • When match reads better than if 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 match line ends in a colon
  • every case line ends in a colon
  • case lines are indented inside the match
  • 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 whenUse if elif else when
One value is compared against many fixed optionsEach branch tests something different
The options are literal values such as commands or codesThe tests are ranges, like marks >= 80
You are pulling pieces out of a pair or a listThere 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.

MistakeWhat you seeWhy it happensFix
Using a bare name as a value to compareSyntaxError: name capture ... makes remaining patterns unreachableA bare name captures the value instead of comparingWrite the literal: case 35:
A bare name in the last caseIt matches everything, with no error at allNothing follows it, so nothing becomes unreachableWrite the literal, or use case _
Putting case _ firstSyntaxError: wildcard makes remaining patterns unreachablecase _ matches anything, so nothing below it could be reachedPut case _ last
Leaving out case _Nothing happens, and no errorA value that matches nothing simply falls throughAdd case _ to catch the rest
Forgetting a colon on match or caseSyntaxError: expected ':'Both lines open a blockEnd both lines with :
Not indenting the case linesIndentationError: expected an indented blockcase lines live inside the match blockIndent each case by four spaces
Using || for "or" in a caseSyntaxError: invalid syntaxPython uses a single | in a patterncase "A" | "B":
Expecting several cases to runOnly one message appearsmatch stops at the first case that fitsCombine the values with |
Using match for rangesCases that never matchA pattern compares shapes and values, not rangesUse if elif else, or add a guard with if
Repeating a caseA branch that silently never runsAn earlier identical case already matchedRemove the duplicate

What to Remember

  • match compares 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 match line and every case line end in a colon, and each block is indented.
  • Only one case ever runs, exactly as in an if elif else chain.
  • A single | inside a pattern means "or", so one case can cover several values.
  • case _ matches anything. It is the else of a match, and it goes last.
  • Without case _, an unmatched value does nothing at all — no output and no error.
  • A bare name in a case captures 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 case can carry an extra if test, called a guard.
  • Use match for one value against fixed options, and if elif else for ranges and unrelated tests.
  • match needs Python 3.10 or newer.

Next: Loops