Question
Python Switch Statement Alternatives: Dictionary Dispatch, if/elif, and match
Question
How can I write a Python function that returns different fixed values according to an input index? In languages with switch or case statements, I would use one of those constructs. What are the recommended Python approaches for this situation?
Short Answer
Python has several clear alternatives to a traditional switch statement. By the end of this page, you will know when to use a dictionary lookup for fixed mappings, if/elif for conditions, and match/case in Python 3.10 and later.
Concept
A switch statement selects one outcome from several alternatives based on a value. Python historically did not have a switch keyword, so Python code commonly expresses the same idea with other language features.
For a value that maps directly to a fixed result, a dictionary is usually the most concise and maintainable choice. A dictionary stores key-value pairs, so an input index can be used as a key.
labels = {
1: "first",
2: "second",
3: "third",
}
result = labels[2] # "second"
Python 3.10 introduced match/case, called structural pattern matching. It can look similar to a switch statement, and it is especially useful when cases involve shapes of data, such as lists, dictionaries, or classes. For a simple fixed lookup, however, a dictionary is often simpler.
if/elif remains the right tool when each branch depends on a comparison or condition rather than exact values. For example, checking whether a score is greater than 90 cannot be represented as a direct dictionary lookup.
Choosing the clearest construct matters because it makes adding cases, handling invalid input, and reviewing code easier.
Mental Model
Think of a dictionary as a labelled set of drawers.
- The input index is the label you look for.
- The associated value is the item inside that drawer.
- If a drawer does not exist, you decide whether to use a default item or report an error.
messages = {"en": "Hello", "fr": "Bonjour"}
Looking up messages["fr"] means: “Open the drawer labelled fr.”
By contrast, an if/elif chain is like asking a sequence of yes-or-no questions until one answer is yes. match/case is like sorting an item into a labelled category, including categories based on its structure.
Syntax and Examples
For a direct mapping from one exact value to another, use a dictionary.
def weekday_name(index):
names = {
0: "Monday",
1: "Tuesday",
2: "Wednesday",
3: "Thursday",
4: "Friday",
}
return names.get(index, "Unknown day")
print(weekday_name(2)) # Wednesday
print(weekday_name(9)) # Unknown day
dict.get(key, default) returns the matching value when key exists. Otherwise, it returns the supplied default.
For a small set of condition-based branches, use if/elif.
def score_band(score):
if score >= 90:
return "excellent"
elif score >= 60:
return "passing"
return "needs improvement"
For exact cases in Python 3.10+, match is also available.
def traffic_action(light):
match light:
case "green":
return "go"
case "yellow":
return "slow down"
case "red":
return "stop"
case _:
return "unknown signal"
The _ case is the catch-all default, similar to default in many switch statements.
Step by Step Execution
Consider this dictionary-based function:
def shipping_cost(zone):
costs = {
"local": 5,
"national": 12,
"international": 25,
}
return costs.get(zone, 0)
price = shipping_cost("national")
Step by step:
- Python calls
shipping_costwithzoneset to"national". - The
costsdictionary is created with three keys. costs.get("national", 0)searches for the key"national".- The key exists, so the lookup produces
12. - The function returns
12. priceis assigned the value12.
If the call were shipping_cost("moon"), the key would not exist and .get(zone, 0) would return the default value 0.
Real World Use Cases
Dictionary lookups and related branching patterns appear in many applications:
- Status labels: Map numeric or string status codes to display text.
status_labels = {200: "OK", 404: "Not Found", 500: "Server Error"} - Configuration: Select a URL, file path, or setting for an environment such as
"development"or"production". - Formatting: Map a currency code to its symbol or decimal precision.
- Command-line tools: Map a command name to the function that handles it.
- APIs: Convert known request types into response handlers.
- Data processing: Categorize known input values before writing reports or importing records.
Use if/elif for value ranges and combined conditions, such as permissions, account balances, or score thresholds. Use match when the decision depends on the form of structured input.
Real Codebase Usage
In production code, developers usually choose the smallest clear construct for the decision.
Fixed values: dictionary lookup
Keep mappings close to configuration or constants, rather than burying them inside a long function.
ROLE_LABELS = {
"admin": "Administrator",
"editor": "Content editor",
"viewer": "Read-only user",
}
def role_label(role):
return ROLE_LABELS.get(role, "Unknown role")
Actions: dictionary dispatch
A dictionary can map values to functions instead of fixed values.
def create_user():
return "Creating user"
def delete_user():
return "Deleting user"
ACTIONS = {
"create": create_user,
"delete": delete_user,
}
def run_action(name):
action = ACTIONS.get(name)
if action is None:
raise ValueError(f"Unsupported action: {name}")
return action()
This pattern avoids a long if/elif chain when each command has a separate handler.
Validation and guard clauses
Validate unexpected input early when an unknown value should be an error rather than silently receiving a fallback.
def priority_rank(priority):
ranks = {"low": 1, "medium": 2, "high": 3}
if priority not in ranks:
raise ValueError("priority must be low, medium, or high")
return ranks[priority]
The correct default behavior is a business decision: use a fallback only when an unknown input is genuinely acceptable.
Common Mistakes
Assuming Python has no switch-like syntax at all
Python 3.10+ has match/case, but it is not always the best replacement. For a simple key-to-value mapping, a dictionary is often shorter.
Using dictionary indexing when unknown input is normal
This raises KeyError if the key is missing:
colors = {1: "red", 2: "blue"}
print(colors[3]) # KeyError
Use .get() when a fallback is appropriate:
print(colors.get(3, "unknown"))
Hiding invalid input with an unsafe default
A default can conceal a bug if an unsupported value should never occur. Raise an error or validate instead.
modes = {"read": "r", "write": "w"}
mode = modes.get("delete", "r") # Silently chooses read mode
For this situation, explicit validation is safer.
Using if/elif for a large fixed mapping
This works but becomes repetitive:
if code == "US":
return "United States"
elif code == "CA":
return "Canada"
A dictionary makes the data easier to scan and update.
Forgetting that dictionary keys must match exactly
"1" and 1 are different keys, as are "Admin" and "admin". Normalize input when necessary.
role = role.strip().lower()
Calling handlers too early in dispatch dictionaries
This is incorrect because the functions run while building the dictionary:
handlers = {"start": start_server(), "stop": stop_server()}
Store the functions without parentheses, then call the selected function later:
handlers = {"start": start_server, "stop": stop_server}
Comparisons
| Approach | Best for | Example | Notes |
|---|---|---|---|
| Dictionary lookup | Exact input mapped to fixed data | {1: "one", 2: "two"} | Concise and easy to extend. |
| Dictionary dispatch | Exact input mapped to an action | {"add": add_item} | Store function objects, then call the selected one. |
if/elif/else | Ranges or complex boolean conditions | if age >= 18: | Best when conditions are not simple equality checks. |
match/case | Exact values or structured patterns | case [x, y]: | Available in Python 3.10+; powerful for data shapes. |
A traditional switch statement generally compares one value against multiple cases. A dictionary most closely replaces that behavior when each case is an exact key and each outcome is fixed data or a handler.
match is not merely a switch clone: it can destructure data.
match point:
case [0, 0]:
print("origin")
case [x, y]:
print(f"point at {x}, {y}")
Use this extra power only when it makes the code clearer.
Cheat Sheet
# Fixed mapping with a safe fallback
value = mapping.get(key, default)
# Fixed mapping where a missing key is an error
value = mapping[key] # raises KeyError if absent
# Conditional branching
if condition:
...
elif other_condition:
...
else:
...
# Python 3.10+ matching
match value:
case "known":
...
case _:
...
# Function dispatch
handlers = {"save": save_file, "load": load_file}
handler = handlers.get(command)
if handler is None:
raise ValueError("Unknown command")
result = handler()
- Use a dictionary for exact key-to-value mappings.
- Use
.get(key, default)only if a fallback is valid. - Use
mapping[key]when missing keys should fail loudly. - Use
if/eliffor comparisons, ranges, and multiple conditions. - Use
match/casefor readable value cases or structured data patterns in Python 3.10+. - Dictionary keys are type-sensitive:
1is not the same key as"1".
FAQ
What replaces a switch statement in Python?
For fixed mappings, use a dictionary. For conditional logic, use if/elif/else. Python 3.10+ also provides match/case.
Is match/case the same as switch in Python?
It can handle simple switch-like cases, but it also supports structural pattern matching, such as matching lists, mappings, and class attributes.
When should I use a dictionary instead of if/elif?
Use a dictionary when you match exact known keys to values or handlers. Use if/elif when the logic uses ranges, comparisons, or combined conditions.
Does dict.get() raise KeyError?
No. It returns None by default, or the fallback value you provide. mapping[key] raises KeyError for a missing key.
Can a dictionary replace switch cases that run functions?
Yes. Map each command to a function object, such as {"start": start_server}. Do not include parentheses when creating the mapping.
Which Python versions support match/case?
Structural pattern matching was added in Python 3.10. Use dictionaries or if/elif when supporting earlier versions.
Should every dictionary lookup have a default?
No. Provide a default only when unknown input is expected and has a valid fallback. Otherwise, allow or raise a clear error.
Mini Project
Description
Build a delivery-zone quote helper for a checkout system. It converts a zone code into a fixed shipping price and clearly handles unsupported zones. This demonstrates the common dictionary-as-switch pattern used for configuration-style mappings.
Goal
Create a function that returns the shipping price for a known delivery zone and raises a helpful error for an unknown zone.
Requirements
Use a dictionary to store at least three zone-to-price mappings.
Create a function that accepts a zone code.
Return the matching price for valid zone codes.
Treat zone codes without regard to leading/trailing whitespace or letter case.
Raise ValueError when the zone code is unsupported.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Add Rows to a Pandas DataFrame in Python
Learn how to add rows to a Pandas DataFrame, why repeated row appends are slow, and when to use loc, concat, or record lists.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.