Emulating Switch/Case – Full Example
This video concludes the course on Emulating Switch/Case in Python. In this lesson you’ll see a real world example of how you to emulate switch/case using a Python dictionary and lambda
functions :
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)()
You’ll also see a potential downside of using this method.
Congratulations, you made it to the end of the course! What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment in the discussion section and let us know.
00:00
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.
00:13 The idea for this would be,
00:16 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?
00:28 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,
00:40 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.
00:49
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.
01:06 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. All right, so this would actually be a little bit shorter.
01:28 What this function would do,
01:31
it would create a dictionary, and then it would define these handler functions with a bunch of lambdas here. 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.
01:50
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
.
02:03
So, you might’ve seen here, this if
—if we fall through all of the cases and none of them match—the Python does an implicit return None
here at the end.
02:14 We could also explicitly add that, and it would probably be better for real-world code, like, 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 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.
03:00
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?
03:16 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.
03:27 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?
03:38 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.
04:10 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?
04:25
Because it can be so foreign. 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.
04:36 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.
Dan Bader RP Team on March 28, 2019
If I understood your example correctly, something like this might work:
def dispatch_dict(op, x, y, z):
return {
'a': lambda: func1(x, y),
'b': lambda: func2(x, y, z),
'c': lambda: func3(x, y)
}.get(op, lambda: None)()
Mahidhar Nyayapati on April 4, 2019
I tried it and it worked… thanks.. In my code I had to return dict in all cases including the default case, so modified it a bit
def func_default():
return {}
def dispatch_dict(op, x, y, z):
return {
'a': lambda: func1(x, y),
'b': lambda: func2(x, y, z),
'c': lambda: func3(x, y)
}.get(op, lambda: func_default() )()
carl on Aug. 12, 2020
Nice tutorial!
Near the end of the full example, you discuss pulling the creation of the dict out of the dispatch function, as an optimization. Just wanted to note, that since the lambda’s you define are directly accessing variables from the enclosing scope, you can’t just cut and paste the dict from the dispatch function to (for example) a global assignment.
For those following along who want to try pulling the dispatch table creation out of the function, you’ll need to populate the dict with lambdas that take arguments x and y, like this:
DISPATCH_TABLE = {
"add": lambda x, y: x + y,
"sub": lambda x, y: x - y,
"mul": lambda x, y: x * y,
"div": lambda x, y: x / y
}
And then you’ll pass the arguments x
and y
from the dispatch function into the lambda, like so.
def dispatch_op(op, x, y):
return DISPATCH_TABLE.get(op, lambda x, y: None)(x, y)
John Kinder on Oct. 6, 2020
There are times where you would like certain conditions to run multiple cases. A switch statement can be controlled with break statements, so in C:
switch(c){
case 1: puts("One... no break so going to two.");
case 2: puts("Two... break"); break;
case 3: puts("Three... break"); break;
case 4: puts("Four... no break so going to default.");
default: puts("Default."); break;
}
If c == 1 I want to execute 1 and 2, thus no break at the end of case 1… same for 4 that cascades to default. I can accomplish the same by updating a received variable, like:
In [1]: def myswitch(c):
...: if c == 1:
...: print('One... no break so going to two.')
...: c = 2
...: if c == 2:
...: print('Two... break')
...: return
...: if c == 3:
...: print('Three... break')
...: return
...: if c == 4:
...: print('Four... no break so going to default')
...: print('Default.')
...: return
...:
In [2]: for i in range(1,6):
...: print(f'{i}:', end=' ')
...: myswitch(i)
...:
1: One... no break so going to two.
Two... break
2: Two... break
3: Three... break
4: Four... no break so going to default
Default.
5: Default.
Here I just update my variable as needed and use separate if’s and returns where I would be using breaks. Really no big deal and only requires the additional step of updating the variable in order to be caught by subsequent if statements and using returns as breaks. Is there a better way to accomplish this though?
Ghani on Oct. 15, 2020
Great course!
AG on Nov. 26, 2020
If the switch case if condition is like “add” in op instead of “add” == op is there any way to still use lamdba in similar way
Dan Bader RP Team on Nov. 26, 2020
@AG: The challenge with an “in
” vs an “==
” check is that Python dictionaries are only fast for “==
” lookups: realpython.com/python-data-structures/#dictionaries-maps-and-hash-tables
For those in
checks I’d probably just write a chained if
-statement, for example:
if "add" in op:
x += y
elif "sub" in op:
x -= y
elif ... # and so on
Become a Member to join the conversation.
Mahidhar Nyayapati on March 28, 2019
How about longer functions that cannot be expressed in single line lambda and the case where each function has different input parameters , such as :
I guess code for func1, func2 and func3 cannot be expressed as a lambda funcion.
Even if they can be expressed as a single line lambda func. idea behind this not to know about expressing complex functions into single line lambdas.
All I want is how to have handler functions with long lines of code in them into a dictionary?