What is the Switch-Case Equivalent in Python?

Janne Kemppainen |

The Python syntax doesn't have the switch-case statement. So what is the equivalent in Python? That's what we'll talk about in this post.

Why no switch-case in Python?

So you might wonder why Python doesn't have a switch-case syntax. It's not that it hasn't been attempted but a satisfying syntax hasn't been proposed yet.

In 2006 Guido van Rossum, the original author of Python, proposed different alternatives for the switch-case syntax in PEP 3103 but they all seemed to have some problems and the idea didn't gain enough popular support. The proposal was therefore rejected.

If you have the time you might find it interesting to read or skim through the proposal to see what exactly were the problem points.

Switch-case in other languages

Before going to the Python details let's start with a language that actually implements the switch-case statement, C++. The syntax is simple, you start a block with switch, define conditions with case and exit from the conditions with break, for example:

switch(i) {
    case 1: cout << "First case";
    break;
    case 2: cout << "Second case";
    break;
    default: cout << "Didn't match a case";
}

The above code uses the value of i to decide what should be printed on the console. If there is no match then the default value will be printed. If the break statement is excluded then the execution would continue on the next line.

switch(i) {
    case 1: cout << "First case";
    case 2: cout << "Second case";
    default: cout << "Didn't match a case";
}

With an input value of 1 the code above would print out all cases. Typically you'd want to have the break statements there, forgetting to add one can be a source for bugs in your code.

Next, let's see what are the options that we have in Python.

If, elif, else

The most basic and easy way to emulate a switch statement is to use plain if else statements. The equivalent of the first example would look like this:

def print_case(value):
    if value == 1:
        print("First case")
    elif value == 2:
        print("Second case")
    else:
        print("Didn't match a case")

The value is compared against every option until a match is found or until the final else statement is reached.

The code is quite easy to reason about but with lots of cases it can become difficult to manage. There are also many state comparisons that we need to write manually which can be error prone. However, this should probably be your choice for simple cases with only a few possible states. This is also what the Python docs suggest as the substitute.

Dictionary

Another way to achieve similar results is to put callables inside a dictionary. This method is also mentioned in the Python docs.

def print_case(value):
    cases = {
        1: lambda: print("First case"),
        2: lambda: print("Second case"),
        3: lambda: print("Third case"),
        4: lambda: print("Fourth case"),
    }
    cases.get(value, lambda: print("Didn't match a case"))()

As you can see this can look a bit cleaner with more options as compared to the if-else statements.

In this code each key in the dictionary contains a lambda function without parameters which then calls the appropriate print function. Lambda functions are anonymous functions that can be stored in variables and then used just as normal functions.

The correct function is fetched using the get method from the dict and then it is called without arguments. The second argument of the get method is the default value which will print a message that a matching case was not found.

Notice the parentheses at the end of the last line. That is the actual function call.

If you don't want to have a default action then this would work too:

def print_case(value):
    cases = {
        1: lambda: print("First case"),
        2: lambda: print("Second case"),
        3: lambda: print("Third case"),
        4: lambda: print("Fourth case"),
    }
    cases[value]()

If you try to call the print_case function with an invalid value such as 5 the code will raise a KeyError. This might be desired so that your program won't continue with an invalid input. Just be sure to handle the error elsewhere in your code.

A more complex example

What if you need to add parameters to the cases?

Let's change our example a bit. Now what if we wanted to create a function called calculate that takes an operator and two operands, and returns the result of the calculation? We could implement it like this:

def calculate(operator, x, y):
    cases = {
        "+": lambda a, b: a + b,
        "-": lambda a, b: a - b,
        "*": lambda a, b: a * b,
        "/": lambda a, b: a / b,
    }
    return cases[operator](x, y)

Each of the lambda functions now has two arguments that are defined before the colon. The final function call then passes the x and y arguments to the selected lambda. Notice that the lambda functions don't contain the return keyword.

Here are some sample calls on the interactive shell.

>>> calculate("+", 50, 21)
71
>>> calculate("-", 50, 21)
29
>>> calculate("*", 50, 21)
1050
>>> calculate("/", 50, 21)
2.380952380952381

In my opinion this solution looks quite clean compared to the if-else option.

Remember that you don't have to use lambda functions at all:

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    return x / y

def calculate(operator, x, y):
    cases = {
        "+": add,
        "-": subtract,
        "*": multiply,
        "/": divide,
    }
    return cases[operator](x, y)

The results are still the same. This time we used normal named functions that are used as objects in the case lookup dict.

If you're really worried about performance then you could move the dictionary generation outside of the function. Then it won't be regenerated each time calculate is called. In practice there shouldn't be much difference.

Conclusion

Hopefully you found this post useful! I have to admit that writing these examples down in a blog format helps me remember this later on when I'm doing real work.

Subscribe to my newsletter

What's new with PäksTech? Subscribe to receive occasional emails where I will sum up stuff that has happened at the blog and what may be coming next.

powered by TinyLetter | Privacy Policy