You must own this product to watch this lesson.

Locked learning resources

You must own this product to watch this lesson.

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please refer to our video player troubleshooting guide for assistance.

Emulating "switch/case" Statements

00:00 I want to talk about a really cool Python trick, and that is a way to emulate switch/case statements in Python.

00:09 What I frequently see in Python code is that people end up with these relatively long if statements—these kind of chained if statements to decide on what action to take based on a condition in their code. To give you an example, let’s say we had this if statement where we had a condition and we wanted to compare it to a certain value and then, “Okay, if we get A, I want to call handle_a(), and if we get a condition B, then we’re going to call handle_b(), and if we get none of these, then we’re going to call handle_default(). Right?

00:50 And you can imagine that this would go on for much longer, and this would get kind of cumbersome, right? It’s a lot of typing, and the reason for that is Python doesn’t really have a switch/case statement with which we could use to condense this a bit and make it more terse and easier to read.

01:14 So, there is actually a way to emulate that switch/case behavior in Python, and I want to show you how to do that. The way to do that is by leveraging the fact that Python has first-class functions. So, functions in Python—they’re just like any other object.

01:28 You can pass them as arguments to other functions, and you can return them from functions as values, and you can store them in variables and store them in data structures.

01:40 So, this is actually a really flexible feature in Python. To give you an example, we can define a simple function here that takes two arguments and just returns the sum of both. I could actually go ahead and say, “Hey, I’m going to store this function in a list that I’m going to call funcs,” and then I could just refer—whoops—I could just refer to that function and call it.

02:11 Right? So if I did that, I would actually get a result that is 5 because it’s adding together 2 and 3, right? I would just—well, with the index here—grab the first function in there—which is myfunc(), which we defined up there—and then I can just call it with the braces syntax.

02:32 So, this is extremely flexible, and this is a really cool feature that’s going to allow us to emulate how a switch/case statement would work. All right. So, let’s come back to our original plan here, which was emulating a switch/case statement. So, now that we know about these first-class functions, what we can do here is actually create a function dictionary that maps from the condition name—or, from the actual condition to the handler function.

03:03 Right? So, something like this where we’re just mapping from the condition to the handler function. Then, I could look up that handler function just by passing the condition as a key into that dictionary, right?

03:25 Then, I could just call the resulting function. And this would actually be—

03:32 sort of—a switch/case statement, right? And we could shorten that further—I mean, you know, we don’t have to give a name to this. Like, we could just do this, and this would already be kind of terse or,

03:48 you know, there’s not a lot of overhead to this. But it has a big downside, and that is we’re going to get a KeyError if the condition isn’t found. There’s no default at the moment, right? Previously with the if statement we had an else all the way at the bottom, and that would allow us to have a default branch that we can take and a default handler that we would call—and we can’t really do that right now.

04:17 So actually, one way around that is—and let me undo some of that—so actually one way around that would be to use the .get() function on the dictionary. We could just call .get(), and then that would either return the value for that key, if it’s found, or it would return a default. In this case, we would just pass in a handle_default function that we’d have to define, and then call that.

04:50 So, this would actually solve the problem with the KeyError, and we’d be able to have a default and have this dispatch based on this function dictionary here, which is already kind of neat. And you can imagine, like, this becomes really useful if you have many, many, many, many functions that you want to map to, or many conditions that you want to map to functions.

05:12 Because then, it actually becomes a lot more efficient than an if / else statement, because an if / else—it needs to try all these conditions in sequence, but with a dictionary, it can actually take a shortcut and do a lookup based on the hash of that condition.

05:29 That can be a lot faster if you’re dealing with many of these mappings. I mean, if you’re only dealing with tens or potentially even hundreds of them, it won’t matter, so it would be a premature optimization, but for a really large number of functions and conditions, this could be extremely helpful.

05:46 So, this could be a really nice trick for you to actually speed up your Python code, to optimize it. So now, this might look kind of weird syntactically, and there’s actually one step we can go beyond this to clean this up, and I want to show you how to do that now. All right, so let’s take a look at a little bit more realistic example, a little bit more complex example. So, I’ve got some code here that is an if statement that would do the dispatch for us.

06:23 The idea for this would be, you know, maybe we’re implementing some kind of virtual machine and want to be able to pass it an operator and two operands, and then it would look up the operator and then carry out some operation based on that, right? Like, it would either add the two numbers, or subtract them, or multiply them, or divide them. So, you know, it’s just a little toy example here. Okay, so just to show you how you would use this function, we’re going to call the function, and then we would pass the instruction or the operator to it—so in this case we want multiplication—and then we would pass two values to it.

07:00 And the result of that—we would expect the result of that to be 16, right? 2 * 8. And this would just kind of go through that if, and say, “Okay, well, we’re looking for the operator == 'mul', so I’m just going to return x * y.” This is how this would work.

07:17 Now, if we wanted to implement the same function using this dictionary as a switch/case emulation of sorts, then it would look a little bit different. And I’m introducing another Python feature here into the mix, so let’s just walk through that, right? So, this would actually be a little bit shorter. What this function would do is it would create a dictionary, and then it would define these handler functions with a bunch of lambdas here.

07:52 So, this is a way for us to not have to define these handle_add(), and handle_sub(), and all these helper functions separately—but we can kind of keep that actual behavior inline, here.

08:06 Then, we’re using—well, we’re creating—this dictionary and then we’re using the .get() method on the dictionary with the operator and our default is just returning None. So, you know, as you might’ve seen here: this ifif we fall through all of the cases and none of them match—then Python does an implicit return None here at the end. Or, you know, we could also explicitly add that, and it would probably be better for real-world code: doing something like this, or just doing something like this where there’s just no way we can fall through. I mean, in this case, they would both be equivalent, so if I wanted to remove that to kind of show how terse this could also be. But, really, here you can see that this is equivalent to this code up here, and it looks kind of nice. I mean, it’s not exactly a switch/case statement, and there’s actually one big downside to this that I want to mention as well, but it works kind of nicely, and it can be a good way to work in these dispatch situations where you want to map some condition to some kind of behavior. It can be a really helpful way to structure your code. Now, one downside to doing it like this is that you’re constructing a dictionary object, and a bunch of lambda or a bunch of functions every time this dispatch_dict() function is called—which is not great, right?

09:36 For memory reasons and for performance reasons, it’s not a good decision. I mean, it won’t matter if you’re not calling this a lot—it honestly won’t matter, and I would go with whatever is cleaner. But one way to get around this would be to take this value—or, this dictionary, and actually store it in some variable in some other place, and then you can refer to it, right? Like, if you have this massive dispatch table mapping from condition to handler, I would just cache that somewhere—keep it in a variable—and then in your actual dispatch code, you would just take that dictionary and look up the function and then call the function, and that way you don’t have to recreate this dictionary every single time the dispatch function is called. But, you know, like I said, if you’re doing this with just a couple of cases and it’s not in, like, a tight loop, like a hot spot in your program—it probably won’t matter, and it would be premature optimization.

10:35 I would go with whatever looks better to you. I mean, another downside here is that this is pretty advanced Python, so if you’re doing this—you know, depending on the skill levels in your team, or the people you’re working with—people might have a hard time dealing with this, right? Because it can be so foreign.

10:52 Like, you need to understand first-class functions, all of these advanced-level features, whereas a nice and clean if statement—it gets the job done and it might actually be the better choice in a lot of situations.

11:03 So, I just want to make sure you are aware of that, but it definitely has its place and it definitely has its applications. And again, I think it’s a really cool feature that teaches you about some of the cool, lesser-known aspects of Python.

tmanai on Jan. 23, 2020

Hello Dan can you please share the IDLE setting that you are using. It makes the IDLE handy and easy to read. Thanks a lot. Taoufik

Dan Bader RP Team on Jan. 23, 2020

@tmanai: Thanks, I’m using Sublime Text in this video and I’ve documented the development setup I use for Python in my Sublime Python course you can find on Real Python.

kalanzun on July 26, 2020

Hi! The example at 7:33 is not exactly equivalent, I think. dispatch_dict actually calculates all 4 operations, discards three and selects one. Whereas dispatch_if only actually calculates the selected operation. This might be an issue if some x or y do not support all operators or when factoring out in a variable. Then it should read {‘add’: lambda x, y: x + y}.get(operator, lambda x, y: None)(x, y)

xuyejuly on Aug. 27, 2020

I didn’t get the “lambda” part inside the dispatch_dict function. Doesn’t the lambda function syntax be: lambda argument: expression. And in this example: it was lambda:expression, and I don’t think by default the x and y would be taken as the argument. I ran this code and it didn’t work at all.

xuyejuly on Aug. 27, 2020

Okay, so I believe the emulation should be like the following with lambda function implemented:

def dispatch_dict(operator):
    return {
        "add": lambda x,y: x+y,
        "sub": lambda x,y: x-y,
        "mul": lambda x,y: x*y,
        "div": lambda x,y: x/y,
        }.get(operator, lambda: None)

func = dispatch_dict("mul")
print(func(5,6))

Then in this example you first get a function “func” with ability to multiply, then call its inside lambda function to multiply the two numbers given as “func“‘s arguments. You have to use “print” to display your result.

Arun Kumar on March 13, 2024

This seems to be a wrong implementation as on python 3.11

The correction which made it work on my system is

def calc(operator:str):
    return {
        '+': lambda x,y: x+y,
        '-': lambda x,y: x-y,
        "*": lambda x,y: x*y,
        "/": lambda x,y: x//y,
        "%": lambda x,y: x%y,
    }.get(operator, lambda:None)
calc('+')(2,3)

lambda expression need argument awareness.

The other way this works is without lambdas

def calc(operator:str, x,y):
    return {
        '+': x+y,
        '-': x-y,
        "*": x*y,
        "/": x//y,
        "%": x%y,
    }.get(operator, None)
calc('+',2,3)

Bartosz Zaczyński RP Team on March 14, 2024

@Arun Kumar The code presented in the video works as expected on the latest release of Python:

Python 3.12.2 (main, Mar  3 2024, 18:44:59) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def dispatch_dict(operator, x, y):
...     return {
...         "add": lambda: x + y,
...         "sub": lambda: x - y,
...         "mul": lambda: x * y,
...         "div": lambda: x / y,
...     }.get(operator, lambda: None)()
... 
>>> dispatch_dict("add", 3, 4)
7

I suspect the reason why it failed for you is that you might have changed the function signature or called it with the wrong list of arguments.

By the way, the lambdas are used intentionally to defer the evaluation of the arithmetic expressions. When you don’t use lambda expressions, you always perform all calculations, which wastes computing resources and may lead to unintentional side effects.

You must own this product to join the conversation.