Question

Python Switch Statement Alternatives: Dictionary Dispatch, if/elif, and match

pythonswitch-statement

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.

Take Quiz

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:

  1. Python calls shipping_cost with zone set to "national".
  2. The costs dictionary is created with three keys.
  3. costs.get("national", 0) searches for the key "national".
  4. The key exists, so the lookup produces 12.
  5. The function returns 12.
  6. price is assigned the value 12.

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.

Take Quiz

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

ApproachBest forExampleNotes
Dictionary lookupExact input mapped to fixed data{1: "one", 2: "two"}Concise and easy to extend.
Dictionary dispatchExact input mapped to an action{"add": add_item}Store function objects, then call the selected one.
if/elif/elseRanges or complex boolean conditionsif age >= 18:Best when conditions are not simple equality checks.
match/caseExact values or structured patternscase [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.

Take Quiz

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/elif for comparisons, ranges, and multiple conditions.
  • Use match/case for readable value cases or structured data patterns in Python 3.10+.
  • Dictionary keys are type-sensitive: 1 is 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more