Quick answer: Python match-case performs structural pattern matching, not only literal switch-style comparison. It can match values, sequences, mappings, classes, OR patterns, and guarded cases, choosing the first successful case.

match and case add structural pattern matching to Python 3.10 and later. They can replace long if/elif chains when code needs to branch by value, shape, or object structure.
The official Python match statement documentation explains the syntax, and PEP 634 defines the pattern matching behavior.
A match statement tests a subject against cases from top to bottom. The first case that matches runs. Use case _: as the default branch when no earlier case should handle the input.
Unlike a basic switch statement in some languages, Python pattern matching can inspect structure. A case can check whether the input is a list of a certain length, a dictionary with particular keys, or an object with selected attributes.
Match Literal Values
The simplest pattern matches exact values such as strings, numbers, booleans, or None.
command = "start"
match command:
case "start":
print("Starting")
case "stop":
print("Stopping")
case _:
print("Unknown command")
This behaves like a readable switch-style branch. The underscore pattern catches anything that was not matched earlier.
Order matters. Put more specific cases before broad default cases so they have a chance to run.
Literal matches are a good fit for command names, status strings, modes, and small enumerations. They keep the branch table compact without hiding the values being handled.
Match Several Values In One Case
Use the | operator to match one of several literal patterns in a single branch.
status = "q"
match status:
case "q" | "quit" | "exit":
print("Leaving")
case "h" | "help":
print("Showing help")
case _:
print("Continuing")
This keeps related choices together. It is useful for command aliases, short options, and small sets of equivalent values.
Keep each branch focused. If the list of accepted values grows too large, a set lookup may be easier to maintain.
The OR pattern should mean that the alternatives really share the same behavior. If two choices start to need different validation or output, split them into separate cases. match/case destructures Python values, whereas Python fnmatch Library Pattern Guide applies shell-style wildcard patterns to names and paths.

Use Guards For Extra Conditions
A guard adds an if condition to a case. The pattern must match first, and then the guard must be true.
value = 42
match value:
case int() if value > 0:
print("positive integer")
case int() if value < 0:
print("negative integer")
case 0:
print("zero")
case _:
print("not an integer")
Guards are good for numeric ranges, extra validation, and checks that are clearer as conditions than as patterns.
Use guards sparingly. If every case needs a long guard, a normal if/elif chain may be simpler.
Guards also make captured names useful. The pattern can bind a value, and the guard can decide whether that bound value is acceptable for the branch.
Match Sequence Patterns
Sequence patterns match lists or tuples by their shape. They can also capture parts of the input.
tokens = ["move", 10, 20]
match tokens:
case ["move", x, y]:
print(f"Move to {x}, {y}")
case ["quit"]:
print("Quit")
case [name, *args]:
print(name, args)
case _:
print("Invalid command")
The first branch matches a three-item command. The *args pattern captures the remaining items in a longer command.
This style works well for tiny command languages, parsed text, and structured list data where the first item describes the action.
Sequence patterns should match the shapes your program actually accepts. Add a default branch for malformed input so unexpected shapes fail clearly.

Match Mapping Patterns
Dictionary-like data can be matched by required keys. Captured names receive the matching values.
event = {"type": "click", "x": 12, "y": 8}
match event:
case {"type": "click", "x": x, "y": y}:
print(f"Click at {x}, {y}")
case {"type": "keypress", "key": key}:
print(f"Key pressed: {key}")
case _:
print("Unhandled event")
The click case requires all three keys. Extra keys can still be present; the pattern only requires the keys it mentions.
Mapping patterns are useful for JSON-like data, events, API payloads, and dictionaries returned by parsers.
They are also safer than indexing keys manually in every branch. A branch only runs when the required keys exist, so missing-key cases can fall through to a default handler.
Match Class Patterns
Class patterns match object types and can inspect attributes. Dataclasses are a clean way to demonstrate this style.
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
point = Point(0, 5)
match point:
case Point(x=0, y=y):
print(f"on y axis at {y}")
case Point(x=x, y=0):
print(f"on x axis at {x}")
case Point(x=x, y=y):
print(f"point {x}, {y}")
The first matching class pattern runs. The example checks special axis cases before the broader point case.
Class patterns are powerful, but they should still be obvious. If a pattern becomes hard to read, move some logic into a well-named method or guard.
This style is most effective for simple data objects where attributes describe the state directly. For objects with complex behavior, method calls may communicate intent better than a large pattern.

Common Match Case Mistakes
Do not use match/case in code that must run on Python versions older than 3.10. Older interpreters will raise a syntax error before the program starts.
Do not put case _: before specific cases. It matches anything, so later cases would never run.
Do not assume a captured name compares against an existing name. Bare names in patterns capture values. Use literals, dotted constants, or guards when you need a comparison.
The practical rule is to use match when the structure of the input matters. Use ordinary conditionals for simple true/false checks, especially when there are only one or two branches.
Basic Literal And OR Patterns
A match statement evaluates its subject once and tries cases from top to bottom. Literal patterns are useful for commands and status values. An OR pattern combines alternatives that should share the same body. Put specific cases before broad patterns.
def label_status(status):
match status:
case 200 | 201:
return "success"
case 404:
return "missing"
case _:
return "other"
print(label_status(201))

Sequence And Mapping Patterns
Patterns can describe the shape of a list or tuple and bind names from it. Mapping patterns match required keys without requiring every key to be listed. This is useful for command payloads, parsed records, and small configuration objects where structure matters more than one exact value.
def describe(event):
match event:
case ["error", message]:
return f"error: {message}"
case {"kind": kind, "value": value}:
return f"{kind}={value}"
case _:
return "unknown"
print(describe(["error", "timeout"]))
Guards And Readable Fallbacks
A guard adds an if condition after a pattern. Use it for a value constraint that is separate from the structural shape. Keep guards short, use a final wildcard fallback when unknown input is expected, and remember that a case with a wildcard does not bind a variable named underscore.
Frequently Asked Questions
Which Python version introduced match-case?
Structural pattern matching was introduced in Python 3.10.
Is match-case the same as a switch statement?
It can handle switch-like literal dispatch, but it also matches sequences, mappings, class patterns, OR patterns, and guards.
What does the underscore mean in match-case?
A standalone underscore is a wildcard pattern that matches any value and is commonly used as the final fallback.
What is a guard in match-case?
A guard is an if condition attached to a case; the pattern must match and the guard must evaluate true for that case to run.
Try not to overwrite inbuilt variables like ‘quit’ it is common conversation to use an underscore to avoid this: ‘quit_’
Yes. I’ve updated the post accordingly. Thank you for bringing it up!
“Also, with a match case, the code becomes more readable and manageable”
Well that depends on the usecase. Although I’m glad Python has match now, there are simple usecases where its overkill. Like that example for showing guards:
match n: case n if n < 0: print("Number is negative") case n if n == 0: print("Number is zero") case n if n > 0: print("Number is positive")This is a good easy example to show how guards work, but obviously in real code you’d probably write this using a simpler if-chain:
if n < 0: print("Number is negative") elif n == 0: print("Number is zero") elif n > 0: print("Number is positive")(and I don’t think this is ant slower cause you’d be performing the same #if-checks as the match-guard version).
TLDR: Don’t replace all your if-else chains with match; match is best for replacing simple
if x == achains, and pattern matching is also helpful (guards maybe), but use your best judgement.Totally agreed
Hi, you’ve an error in the “Match Case Python for Function Overloading” section.Python doesn’t have function overloading (well, there’s multipledispatch); the first definition of calc is just overwritten. And is_int isn’t (re-)assigned in the text of the integer example, but assuming that this happens, you get false positives for values where n^2 == n^3 (e.g. n=0, n=1)
Python 3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 19:00:18) [MSC v.1929 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> def calc(n:int): return (n**2) ... >>> def calc(n:float): return (n**3) ... >>> n = 9.5 >>> is_int = isinstance(n, int) >>> match is_int: ... case True : print("Square is :",calc(n)) ... case False : print("Cube is:", calc(n)) ... # Cube is: 857.375 ... Cube is: 857.375 >>> n = 9 >>> is_int = isinstance(n, int) >>> match is_int: ... case True : print("Square is :",calc(n)) ... case False : print("Cube is:", calc(n)) ... # Square is : 729 ... Square is : 729 >>>Correct. I’ve updated the post accordingly.