Python Switch Case: match case Statement (With Examples)

Python has a switch case statement: since Python 3.10 it’s called match, and each branch is a case. The value is checked against each case in order, and case _ is the default:

match command:
    case "start":
        print("Starting")
    case "stop" | "quit":
        print("Stopping")
    case _:
        print("Unknown command")

Coming from Java, C# or VBA’s Select Case? The ideas carry over, with a few differences that catch people out. The output below is from Python 3.14.7.

Flow diagram of the Python match case statement where the first matching case runs and case underscore is the default
Python checks each case in order, runs the first one that matches and skips the rest.

Python switch case with match: a first example

Here’s a small function that turns HTTP status codes into text:

def http_status(code):
    match code:
        case 200:
            return "OK"
        case 301 | 302:
            return "Redirect"
        case 404:
            return "Not Found"
        case _:
            return "Something else"

for code in (200, 302, 404, 500):
    print(code, "->", http_status(code))

Output:

200 -> OK
302 -> Redirect
404 -> Not Found
500 -> Something else
Command Prompt showing a Python switch case using match case to map HTTP status codes to names
Each code goes to the first case that matches; 500 falls through to case _.

match code: names the value to test, and each case gives a pattern. The first pattern that fits wins, its block runs, and the rest are skipped.

case 301 | 302: matches either value, which is how you handle several values at once. case _: matches anything, so it plays the role of default.

Does Python have a switch statement?

It does now, but only since Python 3.10. Before that, match was simply a syntax error:

import sys

status = 404
match status:
    case 404:
        print("matched 404 on Python", sys.version.split()[0])

Output:

  File "switch_version.py", line 4
    match status:
          ^
SyntaxError: invalid syntax
matched 404 on Python 3.14.7
Command Prompt showing Python match case raising SyntaxError on Python 3.8 and running on Python 3.14
The same file fails on Python 3.8 and runs on current Python.

I ran the same file on Python 3.8 and on current Python. 3.8 stops at match status: with SyntaxError: invalid syntax, and 3.9 does the same.

So if a teammate gets that error, the fix is usually their Python version, not the code. Checking which Python version you have takes one command.

If you’re coming from VBA or SQL, here’s how Select Case translates:

VBA / SQLPython match
Select Case statusmatch status:
Case 200case 200:
Case 301, 302case 301 | 302:
Case 90 To 100case s if 90 <= s <= 100:
Case Elsecase _:
CASE WHEN ... THEN ... END (SQL)match, or a conditional expression for one line

That history is why so many older tutorials say Python has no switch. It’s also why the dictionary approach further down is still worth knowing.

Match multiple values in one case

Join values with | to share one block. This is the Python answer to stacking several case labels in other languages:

def day_type(day):
    match day.lower():
        case "saturday" | "sunday":
            return "weekend"
        case "monday" | "tuesday" | "wednesday" | "thursday" | "friday":
            return "weekday"
        case _:
            return "not a day"

for day in ("Sunday", "wednesday", "Funday"):
    print(f"{day:<10} -> {day_type(day)}")

Output:

Sunday     -> weekend
wednesday  -> weekday
Funday     -> not a day

| means “or” inside a pattern. Calling .lower() before matching makes the comparison ignore capital letters, so Sunday and sunday both land in the weekend case.

There’s more on comparing text without caring about case in case-insensitive string comparisons.

A switch case driven by user input

Menus are the classic use. input() always returns a string, so match against strings, not numbers:

choice = input("Pick 1) balance  2) deposit  3) quit: ").strip()

match choice:
    case "1":
        print("Your balance is $1,250.00")
    case "2":
        print("How much would you like to deposit?")
    case "3" | "q" | "quit":
        print("Goodbye")
    case _:
        print(f"{choice!r} isn't an option")

Output:

Pick 1) balance  2) deposit  3) quit: How much would you like to deposit?
Command Prompt showing a Python switch case menu where the user types 2 and match case picks the deposit option
Typing 2 at the prompt runs the case "2" block.

The captured output above shows no 2, because typed input isn’t echoed there; the screenshot shows it at the prompt. It arrived as the text "2", so case "2" matched.

case "2" is the important part. A case 2: with a number would never match input, which is a common reason a menu seems to ignore every choice.

.strip() removes stray spaces, and case "3" | "q" | "quit": accepts several ways of saying the same thing.

The default case in a Python switch: case _

case _: catches everything the earlier cases didn’t. Leave it out, and nothing happens for unmatched values:

def describe(x):
    match x:
        case 1:
            return "one"
        case 2:
            return "two"

print(describe(1))
print(describe(9))        # no case matched and there's no case _

Output:

one
None

With no matching case and no case _, Python silently skips the whole match. Here the function then ends without a return, so you get None.

That’s rarely what you want, so I add case _: to almost every match, even if it only raises an error for values that should never happen.

Why a Python switch case needs no break

In C or Java, forgetting break makes execution fall into the next case. Python’s match never falls through:

def label(n):
    match n:
        case int():
            return "any integer"
        case 5:
            return "exactly five"     # never reached for 5: the first match wins

print("label(5) ->", label(5))
print()

seen = []
for n in range(10):
    match n:
        case 3:
            break                     # this leaves the for loop, not just the match
        case _:
            seen.append(n)
print("break inside match stopped the loop at:", seen)

Output:

label(5) -> any integer

break inside match stopped the loop at: [0, 1, 2]

case int() matched 5 first, so case 5 was never checked. Only one block ever runs, which means there’s nothing to break out of.

If you do write break inside a match that sits in a loop, it breaks the loop, as the second part shows. Order your cases from most specific to most general.

Match ranges and conditions with guards

A plain case compares one value. For ranges, add an if guard to the case:

def grade(score):
    match score:
        case s if s >= 90:
            return "A"
        case s if s >= 80:
            return "B"
        case s if s >= 70:
            return "C"
        case _:
            return "below C"

for score in (95, 84, 71, 50):
    print(score, "->", grade(score))

Output:

95 -> A
84 -> B
71 -> C
50 -> below C

case s if s >= 90: captures the value as s and only matches when the condition is true. The cases are still tried in order, so each one only needs its lower bound.

For a handful of ranges like this, an if/elif chain is just as clear. Guards shine when you combine them with the structural patterns below.

The bare-name trap: why case RED matches everything

This is the mistake almost everyone makes once. A plain variable name in a case doesn’t compare against that variable:

from enum import Enum

RED = "red"

def color_bare(c):
    match c:
        case RED:                # a bare name CAPTURES whatever c is
            return "is red"

print("bare name:  ", color_bare("blue"))

class Color(Enum):
    RED = "red"
    BLUE = "blue"

def color_dotted(c):
    match c:
        case Color.RED:          # a dotted name is compared by value
            return "is red"
        case Color.BLUE:
            return "is blue"
        case _:
            return "unknown"

print("dotted name:", color_dotted(Color.BLUE))
print()

code = """
def color(c):
    match c:
        case RED:
            return "red"
        case "blue":
            return "blue"
"""
try:
    compile(code, "<example>", "exec")
except SyntaxError as err:
    print("SyntaxError:", err.msg)

Output:

bare name:   is red
dotted name: is blue

SyntaxError: name capture 'RED' makes remaining patterns unreachable
Command Prompt showing the Python match case bare name capture trap and the fix with a dotted Enum name
A bare name captures any value; a dotted name like Color.RED is compared by value.

case RED: doesn’t mean “equals the constant RED”. It means “match anything and store it in a new variable called RED”, so "blue" was reported as red.

When other cases follow, Python spots it and refuses to run: name capture 'RED' makes remaining patterns unreachable. On its own, though, it matches silently.

The fix is a dotted name. Color.RED from an Enum, or constants.RED from a module, is compared by value, which is what you meant.

Match types, lists and dictionaries with case

This is where match goes far beyond a classic switch. Cases can check the type and shape of a value:

def kind(value):
    match value:
        case bool():             # must come before int(): bool is a kind of int
            return "bool"
        case int() | float():
            return "number"
        case str():
            return "text"
        case [x, y]:
            return f"pair of {x} and {y}"
        case {"action": "buy", "qty": int(qty)}:
            return f"buy order for {qty}"
        case _:
            return "something else"

for value in (True, 42, 2.5, "hi", [1, 2], {"action": "buy", "qty": 3, "note": "rush"}, {"action": "buy", "qty": "3"}):
    print(f"{value!r:<42} -> {kind(value)}")
print()

def is_one(x):
    match x:
        case 1:
            return "matched case 1"
        case _:
            return "no match"

print("match True, case 1 ->", is_one(True))

Output:

True                                       -> bool
42                                         -> number
2.5                                        -> number
'hi'                                       -> text
[1, 2]                                     -> pair of 1 and 2
{'action': 'buy', 'qty': 3, 'note': 'rush'} -> buy order for 3
{'action': 'buy', 'qty': '3'}              -> something else

match True, case 1 -> matched case 1
Command Prompt showing Python match case class patterns list patterns and dictionary patterns
Type checks, list shapes and dictionary keys, all in one match.

case int(): matches any integer, and case [x, y]: matches a two-item list and unpacks it. A dictionary pattern checks only the keys you list and ignores extra ones like note.

int(qty) inside the dictionary pattern also checks the type, which is why the order with "3" as a string fell through. Put bool() before int(), since True is also an int.

The last line is a subtle one: match True hits case 1, because number cases compare with == and True == 1. The reverse, match 1 against case True, doesn’t match, since True is compared by identity. The difference between is and == explains why.

Switch case with a dictionary

Before Python 3.10, a dictionary was the standard switch replacement. It’s still the best choice when each case just picks a value or a function:

def add(a, b): return a + b
def sub(a, b): return a - b
def mul(a, b): return a * b

operations = {"+": add, "-": sub, "*": mul}

def calculate(op, a, b):
    func = operations.get(op)
    if func is None:
        return f"unsupported operator {op!r}"
    return func(a, b)

print(calculate("+", 8, 5))
print(calculate("*", 8, 5))
print(calculate("/", 8, 5))

Output:

13
40
unsupported operator '/'

Each key maps to the function to call, and .get() returns None for a missing key, which becomes the default branch. Adding an operator is one new dictionary entry.

For more on building dictionaries like this, see the Python dict() function.

match case vs if-elif vs dictionary: which is faster?

You’ll read that match is faster than if/elif. I timed all three on the same four cases:

import timeit

setup = """
def with_match(x):
    match x:
        case 1: return "a"
        case 2: return "b"
        case 3: return "c"
        case 4: return "d"
        case _: return "z"

def with_if(x):
    if x == 1: return "a"
    elif x == 2: return "b"
    elif x == 3: return "c"
    elif x == 4: return "d"
    else: return "z"

TABLE = {1: "a", 2: "b", 3: "c", 4: "d"}
def with_dict(x):
    return TABLE.get(x, "z")
"""

for name in ("with_match", "with_if", "with_dict"):
    best = min(timeit.repeat(f"{name}(4); {name}(9)", setup=setup, number=500_000, repeat=3))
    print(f"{name:<11} {best:.3f} s for a million calls")

Output:

with_match  0.104 s for a million calls
with_if     0.091 s for a million calls
with_dict   0.079 s for a million calls
Command Prompt timing Python match case against if elif and a dictionary lookup
On simple value checks, match was the slowest of the three in this run.

On simple value comparisons, match was not the fastest here; the dictionary lookup was, and if/elif beat match too. Exact numbers move between runs, but that order is what I saw.

The differences are tiny per call, so choose on readability. Use match for shapes and types, a dictionary for simple lookups, and if/elif for a few conditions.

If you’re working through Python’s control flow, these fit well alongside this one:

Frequently asked questions

Does Python have a switch case statement?

Yes. Python 3.10 added the match statement, where each branch is a case. Older versions don’t have it and raise SyntaxError.

How do I write a default case in a Python switch?

Use case _: as the last case. It matches anything the earlier cases didn’t.

How do I match multiple values in one case?

Separate them with |, for example case "saturday" | "sunday":.

Do I need break in a Python match case?

No. Only the first matching case runs, so there’s no fall-through. A break inside match exits the surrounding loop.

Why does case NAME match every value?

A bare name is a capture pattern that stores any value in that variable. Use a dotted name like Color.RED to compare against a constant.

Is match case faster than if-elif?

Not for simple value checks: in my test if/elif and a dictionary lookup were both faster. Pick match for readability and patterns.

What replaced switch case before Python 3.10?

A dictionary mapping keys to values or functions, or an if/elif chain. The full pattern syntax is in the Python match statement tutorial.