Python 3.10 and later implement switch-style branching with the match/case statement, formally called structural pattern matching. It handles ordinary value choices, multiple alternatives, conditions, and the shape of sequences, mappings, and objects. Python 3.9 and older cannot parse this syntax, so use if/elif or dictionary dispatch there.
def describe_status(status):
match status:
case 200:
return "OK"
case 400 | 401:
return "Request or authorization problem"
case 404:
return "Not found"
case _:
return "Other status"
Cases are checked from top to bottom. The first pattern that matches (and whose optional guard is true) runs; Python does not fall through into later cases.
Does Python have switch-case?
Yes, in practical terms. Python 3.10 added match/case, a language feature that covers the role commonly filled by switch in C-like languages while also matching data structure. The current language reference documents its syntax and semantics at docs.python.org/3/reference/compound_stmts.html. The Python 3.10 tutorial introduces it as an expression compared with successive patterns in case blocks (official tutorial).
A match statement evaluates its subject once, then tests each case in source order. It is not a function that returns a value automatically, so return or assign inside each suite as needed.
#1 Best Overall
Basic match/case syntax
Match exact values
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Other error"
for code in (200, 404, 418, 500):
print(code, http_error(code))
Literal patterns such as integers and strings are compared with equality. The special literals None, True, and False use identity semantics. The underscore is not a value to compare: it is the wildcard pattern.
Add the default branch
case _: catches anything not handled earlier. It is the switch-style default branch:
def classify_status(status):
match status:
case 200:
return "success"
case 400 | 401:
return "client or authentication problem"
case _:
return "unhandled status"
A wildcard is optional. If no case matches and no case _: appears, the match statement does nothing and execution continues with the next statement.
Combine several values with |
An OR pattern sends several alternatives to one suite:
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesdef permission_message(code):
match code:
case 401 | 403:
return "Authentication or permission problem"
case 404:
return "Resource not found"
case _:
return "Another response"
Each alternative must bind compatible names if you use captures. For example, alternatives that bind different sets of names cannot form one valid OR pattern.
Guards: add a condition after a pattern
A guard is an if condition attached to a case. Python first checks the pattern, then evaluates the guard. If the guard is false, matching continues with the next case.
def describe_number(value):
match value:
case int(number) if number > 0:
return "positive integer"
case int(number) if number < 0:
return "negative integer"
case 0:
return "zero"
case _:
return "not an integer"
Put more specific patterns before broad ones. A broad pattern or wildcard placed first makes later cases unreachable in practice.
Rank #2
Structural pattern matching
Match a sequence and unpack fields
Patterns can validate a sequence’s length and contents while binding selected elements:
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →def handle_command(text):
match text.split():
case ["quit"]:
return "Goodbye"
case ["go", direction]:
return f"Moving {direction}"
case ["get", item]:
return f"Taking {item}"
case _:
return "Unrecognized command"
print(handle_command("go north"))
print(handle_command("get lantern"))
The ["go", direction] pattern requires a two-item sequence whose first item is the string "go"; the second item is bound to direction. A starred name can absorb the remainder, for example ["run", *arguments].
Match mappings
Mapping patterns test required keys and bind their values. Extra keys are allowed unless you separately reject them:
def event_text(event):
match event:
case {"type": "login", "user": user}:
return f"{user} logged in"
case {"type": "error", "code": code, "message": message}:
return f"Error {code}: {message}"
case _:
return "Unknown event"
Match class instances
Class patterns can inspect attributes declared by the class’s positional pattern configuration or by keyword:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def location(point):
match point:
case Point(0, 0):
return "origin"
case Point(x, 0):
return f"x-axis at {x}"
case Point(0, y):
return f"y-axis at {y}"
case Point(x, y):
return f"({x}, {y})"
case _:
return "not a point"
This structural capability is the main reason to choose match over a long list of equality tests. PEP 634 specifies the normative behavior (PEP 634), while the practical tutorial is covered in PEP 636.
Free tools Windows power users keep installed
One-click scans. No signup required.
Capture names versus constants
A bare name in a pattern is a capture, not a comparison with an existing variable:
command = "quit"
match command:
case command: # captures any value; effectively matches everything
print("This is not a constant comparison")
Use a literal or a qualified name for a constant:
from enum import Enum
class Commands(Enum):
QUIT = "quit"
HELP = "help"
def execute(command):
match command:
case Commands.QUIT:
return "Goodbye"
case Commands.HELP:
return "Help text"
case _:
return "Unknown command"
The qualified enum members are value patterns, so they test the subject rather than capturing it. PEP 634 explains this distinction and the other pattern forms in detail.
Does match/case fall through?
No. Once a pattern matches and its guard (if any) succeeds, only that case suite executes. Execution resumes after the entire match statement. To share behavior, put alternatives in one OR pattern or call a common function; do not add C-style break statements.
def access_message(role):
match role:
case "owner" | "admin":
return "Full access"
case "editor":
return "Edit access"
case _:
return "Read-only access"
Choosing match, if/elif, or a dictionary
| Need | Recommended form | Reason |
|---|---|---|
| A few arbitrary boolean, range, or compound conditions | if/elif |
Conditions are direct and familiar. |
| Exact choices or several values sharing an action | match/case on Python 3.10+ |
Literal patterns, OR patterns, wildcard, and guards make the cases explicit. |
| Branching on sequence, mapping, or object shape while extracting fields | match/case |
The pattern validates structure and binds components in one operation. |
| Python 3.9 or older | if/elif or dictionary dispatch |
Older interpreters cannot parse match syntax. |
| Simple key-to-value or key-to-function lookup | Dictionary | Compact direct dispatch can be clearer than a statement. |
Dictionary dispatch example
def say_hello():
return "Hello"
def say_goodbye():
return "Goodbye"
actions = {
"hello": say_hello,
"goodbye": say_goodbye,
}
def run(action):
function = actions.get(action)
return function() if function else "Unknown action"
A dictionary is an alternative technique, not an implementation of match semantics. It does not inherently match structure, apply ordered guards, or provide a wildcard pattern.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPython-version requirements and migration
The grammar for match/case was added in Python 3.10. A 3.9 (or earlier) interpreter fails while parsing the file, before your code can choose a fallback. Check the runtime that actually executes your application:
python --version
python3 --version
If you publish a package, set its minimum Python version in the project metadata and CI configuration, or keep the implementation in if/elif and dictionary forms until all supported runtimes are 3.10 or newer. Do not hide 3.10 syntax in a module that an older interpreter must import.
Common errors and reliable fixes
A bare constant name matches everything
Symptom: a supposedly specific case runs for every input, or the compiler reports a capture that makes later patterns unreachable.
Fix: replace case RED: with a literal such as case "red": or a qualified constant such as case Colors.RED:.
Expecting fall-through
Symptom: code expects two case suites to run.
Fix: combine values with |, or call shared code from one case. No implicit fall-through exists.
No action for an unmatched value
Symptom: a function returns None or silently continues.
Fix: add case _: with an explicit return, error, log, or other policy. Omitting it is valid only when doing nothing is intentional.
Syntax error on an older interpreter
Symptom: Python 3.9 or earlier reports invalid syntax at match.
Fix: upgrade the runtime or rewrite the branch with if/elif or a dictionary. A compatibility shim cannot make the new grammar parse.
Cases are in the wrong order
Symptom: a later, more specific case never executes.
Fix: place narrow literal, sequence, mapping, or class patterns before broad patterns and guards; keep case _: last.
Relying on failed partial-match bindings
Symptom: code reads a name after a pattern failed partway through.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
Fix: treat bindings from failed partial matches as unspecified. Keep subsequent logic independent of whether such names happen to exist or retain an earlier value; this is the guidance in the current language reference.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Testing and performance considerations
Test every intended case, the wildcard path, wrong types, malformed structures, and boundary values for guards. For command parsers, include extra tokens and missing fields. Static analysis can catch unreachable or irrefutable patterns, but unit tests should verify the behavior your application requires.
Python’s specification defines matching behavior, not a universal speed advantage over if/elif or dictionary lookup. Choose the clearest form and benchmark the complete workload on the Python version and data your application uses when performance matters. PEP 622 discusses the background and rationale for the design (PEP 622).
Or skip the browser setup
If your Python application also needs website screenshots for tests, documentation, or event-driven workflows, ScreenshotNeo provides a one-request API instead of requiring you to configure a headless browser. See the ScreenshotNeo documentation for all parameters.
Recommended Free Tools
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Before capture, cookie or consent banners, newsletter popups, and chat widgets are removed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; the response identifies the page verdict and billing result in X-Page-Verdict and X-Billed headers. ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Frequently Asked Questions
Can a match statement return a value directly?
No. A match statement is a statement, so each case must return, assign, yield, or otherwise produce the result explicitly; wrap it in a function when you want a returned value.
Can patterns match types as well as values?
Yes. Class patterns such as int(number) can check a type and bind its value, and custom class patterns can inspect declared attributes.
What happens when a guard is false?
That case is skipped and Python continues testing the following cases in source order.
Are dictionary keys in mapping patterns required to be the only keys?
No. A mapping pattern requires the keys it names but permits additional keys unless your code separately checks for an exact mapping.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

