Python The match Statement

Use match and case to branch on the value and shape of data, with guards, captures and a wildcard fallback.

Lesson 16 of 107Beginner12 min read

The match statement compares a value, called the subject, against a series of case patterns and runs the first one that fits. It arrived in Python 3.10 and is often described as Python's switch statement, but it does more: patterns can check the shape of data, such as a tuple of two items or a dictionary with certain keys, and pull values out of it at the same time.

Python
command = "stop"
match command:
    case "start":
        print("Starting")
    case "stop":
        print("Stopping")
    case _:
        print("Unknown command")

This lesson covers checking your Python version, the main pattern types, guards that add extra conditions, and the capture rules that surprise people coming from other languages. The examples route messages in a parcel depot.

Availability

match is new syntax, so it is checked when Python parses the file. On Python 3.9 or older, a file containing match fails with SyntaxError before a single line runs; you can't guard it with an if at run time. Check your version first:

Terminal
python --version
Python
import sys
print(sys.version_info >= (3, 10))

If your code must support older versions, use an if/elif chain or a dictionary lookup instead. match and case are soft keywords: they only act as keywords at the start of a match statement, so existing code that uses match as a variable name, such as match = re.search(...), keeps working.

Patterns

Each case holds a pattern. The common kinds are:

PatternExampleMatches
Literalcase "stop":, case 404:an equal value
OR patterncase "y" | "yes":any of the alternatives
Sequencecase ("deliver", qty):a two-item sequence starting with "deliver"
Starcase ["scan", *codes]:a sequence of any length, rest captured
Mappingcase {"type": "return", "id": pid}:a dict with those keys (extra keys allowed)
Classcase Parcel(weight=w):an instance of Parcel, capturing an attribute

Sequence patterns match lists and tuples but deliberately not strings. Mapping patterns ignore any extra keys, which suits JSON messages that carry more fields than you need. This structural checking is what makes match more than a switch: one case can verify the type, the length and specific values, and unpack the rest.

Guards

A guard is an if condition written after a pattern. The case runs only if the pattern matches and the guard is true:

Python
match message:
    case ("deliver", qty) if qty > 0:
        print("Pack", qty)
    case ("deliver", qty):
        print("Invalid quantity:", qty)

The guard is checked only after the pattern succeeds, so it can use the captured qty. If the guard is false, Python moves on to the next case. A guard can't rescue a pattern that didn't match: ("collect", 3) never reaches the guard of a ("deliver", qty) case.

Put more specific cases first. Python tries cases from top to bottom and stops at the first match, exactly like an if/elif chain.

Captures

A bare name in a pattern captures whatever is in that position; it does not compare against an existing variable with that name:

Python
LIMIT = 10
match 25:
    case LIMIT:          # captures 25 into LIMIT; matches anything
        print(LIMIT)     # 25

Python actually rejects this when another case follows, with "name capture 'LIMIT' makes remaining patterns unreachable". To compare against a constant, use a dotted name (case Limits.MAX:), an enum member, a literal, or a guard (case x if x == LIMIT:).

The underscore _ is the wildcard: it matches anything and binds nothing. case _: is the conventional fallback and must come last, since every later case would be unreachable. Inside larger patterns, _ ignores positions you don't care about, as in case ("deliver", _):.

Independent practice and a worked check

Task: route parcel commands with a sequence pattern, a positive quantity guard and a final fallback. Handle ("deliver", n) with n > 0, ("cancel", parcel_id), and anything else.

Python
def handle(command):
    match command:
        case ("deliver", qty) if qty > 0:
            return f"Deliver {qty}"
        case ("cancel", parcel_id):
            return f"Cancel {parcel_id}"
        case _:
            return "Check the message"

for cmd in [("deliver", 2), ("deliver", 0), ("cancel", "P-7"), ("lost",)]:
    print(handle(cmd))

This prints Deliver 2, Check the message, Cancel P-7 and Check the message. The zero quantity matches the pattern but fails the guard, so it falls through to the wildcard. For the worked check, a bare name in a pattern normally captures a value; it does not compare against a variable of the same name.

Reference and next practice

PEP 636 is the official pattern matching tutorial, and the match statement reference defines every pattern. Revisit if, elif and else for the alternative, then practise with the Python exercises and Python quiz.

The match Statement Summary

  • Availability: match is structural pattern matching introduced in Python 3.10.
  • Patterns: Literal, sequence and mapping patterns inspect values and their structure.
  • Guards: A case guard checks an additional condition after its pattern succeeds.
  • Captures: A bare name captures the subject while underscore ignores it.

The match Statement Examples

Each example shows the code, the output it produces and what each part does.

Example 1

The match Statement in a small program

Python
message = ("deliver", 4)
match message:
    case ("deliver", quantity) if quantity > 0:
        print("Pack", quantity)
    case _:
        print("Check the message")
Output
Pack 4
Line-by-line explanation
  1. Line 1The subject is a two-item tuple.
  2. Line 3The sequence pattern matches the literal "deliver", captures 4 into quantity, and the guard confirms it is positive.
  3. Lines 5 to 6The wildcard catches every message the first case didn't handle.
Example 2

Routing JSON-style messages

Python
def route(msg):
    match msg:
        case {"type": "deliver", "qty": int(qty)} if qty > 0:
            return f"Pack {qty}"
        case {"type": "return", "id": parcel_id}:
            return f"Log return {parcel_id}"
        case {"type": "scan", "codes": [first, *rest]}:
            return f"Scan {first} and {len(rest)} more"
        case {"type": str(kind)}:
            return f"Unsupported type: {kind}"
        case _:
            return "Malformed message"

print(route({"type": "deliver", "qty": 3, "van": 7}))
print(route({"type": "return", "id": "P-19"}))
print(route({"type": "scan", "codes": ["A1", "B2", "C3"]}))
print(route({"type": "deliver", "qty": "3"}))
print(route(["deliver", 3]))
Output
Pack 3
Log return P-19
Scan A1 and 2 more
Unsupported type: deliver
Malformed message
Line-by-line explanation
  1. Line 3int(qty) is a class pattern: it matches only if the value is an int, then captures it. The extra "van" key is ignored.
  2. Line 7A nested sequence pattern captures the first code and the rest as a list.
  3. Line 17"3" is a string, so the first case fails and the general {"type": str(kind)} case catches it.
  4. Line 18A list isn't a mapping, so only the wildcard matches.

Common The match Statement Mistakes

Expecting a bare name to compare with a variable

Don't
EXPRESS = "express"
match service:
    case EXPRESS:         # captures anything; SyntaxError with later cases
        print("Priority")
    case _:
        print("Standard")
Do
match service:
    case "express":
        print("Priority")
    case _:
        print("Standard")

A bare name always captures. Compare with a literal, a dotted constant such as Service.EXPRESS, or a guard.

Putting the wildcard first

Don't
match code:
    case _:
        print("Other")
    case 404:             # SyntaxError: wildcard makes this unreachable
        print("Not found")
Do
match code:
    case 404:
        print("Not found")
    case _:
        print("Other")

The match Statement in Real Projects

Where match statements earn their place

match is most useful when data has several possible shapes: command-line commands with different arguments, events from a message queue, parsed JSON from a web API, or nodes in a syntax tree. A single case replaces several lines of isinstance checks, length checks and indexing.

For simple value lookups, such as turning a status code into a word, a dictionary is still shorter, and for range checks like grades an if/elif chain reads better. Many teams adopt match gradually, using it where it removes nested conditionals and keeping everything else as it was.

The match Statement FAQ

Does Python have a switch statement?

Since Python 3.10, match covers everything a switch statement does and more. Before 3.10, the usual alternatives were if/elif chains and dictionaries that map values to results or functions. There is no fall-through between cases, so no break is needed.

Which Python version do I need for match case?

Python 3.10 or later. On older versions, any file containing a match statement fails to parse with SyntaxError, so the code can't even start. Check with python --version.

Can one case match several values?

Yes. Separate alternatives with |: case "y" | "yes" | "Y":. If you need the matched value, add as: case ("y" | "yes") as answer:.

Is match faster than if/elif?

Not meaningfully for typical code. Python checks cases in order, much like an if chain. Choose match for readability when you branch on the structure of data, not for performance.

Can I match on the type of a value?

Yes, with class patterns: case int(): matches any integer and case str(text): matches a string and captures it. For your own classes, case Point(x=0, y=y): checks the type and attributes at once.

The match Statement Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

What does a bare name normally do in a match pattern?

Multiple choicemedium

Which review concern is valid for the match statement?

The match Statement Quiz

2 questions to check what you learned.

  1. What does a bare name normally do in a match pattern?

    • Look up an existing constant
    • Capture a value
    • Python ignores the distinction
  2. Which statement correctly describes guards?

    • A case guard checks an additional condition after its pattern succeeds.
    • Only the example output matters, not the rule
    • Every operation has the same behavior