This post is part of the Powerful Python series where I talk about features of the Python language that make the programmer’s job easier. The Powerful Python page contains links to more articles as well as a list of future articles.
In the months since this was written, I have received a number of comments and learned more about programming languages in general. I put some of the new things I learned into a revised post on the topic which you might be interested in.
A switch-case statement is a useful programming language that lets you control the flow of the program based on the value of a variable or expression. In particular, if the variable or expression that you’re testing has a number of different of possible values, you could execute a block of code for each separate value. Here’s a simple example in C (courtesy of Wikipedia):
switch(n) {
case 0:
printf("You typed zero.\n");
break;
case 1:
case 9:
printf("n is a perfect square\n");
break;
case 2:
printf("n is an even number\n");
case 3:
case 5:
case 7:
printf("n is a prime number\n");
break;
case 4:
printf("n is a perfect square\n");
case 6:
case 8:
printf("n is an even number\n");
break;
default:
printf("Only single-digit numbers are allowed\n");
break;
}
Based on the value of the variable n, a different message will show up on the standard output. The default case is executed when the value of the variable doesn’t match anything for which a case is defined. Notice that some cases have a break statement while others don’t. If a particular case block is executed and no break is found, then all the following case blocks will be executed until a break is found.
The switch-case statement comes in handy when you’re writing a language parser like I am now. For simple languages, like most programming languages, each token in a sample of the language can be followed only by a very limited number of possible tokens. My putting the options of possible following tokens in a switch case statement, you have an easy mechanism for performing different tasks based on what token is actually found. For example, if it’s a variable name, you check if its been declared before, if it’s an operator, perform the corresponding operation etc.
Unfortunately, my language of choice for the time being is Python, which doesn’t come with a typical switch-case statement. One simple substitute is using a string of if-else blocks, with each if condition being what would have been a matching case. Here is part of the above code in Python using if-else blocks.
if n == 0:
print "You typed zero.\n"
elif n== 1 or n == 9 or n == 4:
print "n is a perfect square\n"
elif n == 2:
print "n is an even number\n"
elif n== 3 or n == 5 or n == 7:
print "n is a prime number\n"
It certainly works and should be pretty easy to work, but it’s not a very elegant solution. Especially if you have more than a handful of cases, you’re going to have a very long string if-else cases. Since each of the if conditions must actually be checked, you might run into performance issues if this is a vital part of your code.
The Pythonic solution is to make use of Python’s powerful dictionaries. Also known as associative arrays in some languages, Python’s dictionaries allow a simple one-to-one matching of a key and a value. When used as part of a switch case statement, the keys are what would normally trigger the case blocks. The interesting part is that the values in the dictionaries refer to functions that contain the code that would normally be inside the case blocks. Here’s the above code rewritten as a dictionary and functions:
options = {0 : zero,
1 : sqr,
4 : sqr,
9 : sqr,
2 : even,
3 : prime,
5 : prime,
7 : prime,
}
def zero():
print "You typed zero.\n"
def sqr():
print "n is a perfect square\n"
def even():
print "n is an even number\n"
def prime():
print "n is a prime number\n"
Now that you have the switch case setup, you can actually use it by simply doing a dictionary lookup:
options[num]()
Thanks to the fact that Python functions are first class values, you can use the functions as the values of the dictionary and then call them via dictionary lookup.
The advantage of this method is that it is generally cleaner than a long line of if-elses and someone reading your code can simply ignore the functions they are not interested in. Performance-wise, the Python dictionary lookup will almost certainly be more efficient than any solution you can rig yourself (with perhaps the exception of custom C code, but if you can do that, why are you reading this?). However, except a penalty associated with calling and coming back from a function. In fact, it’s the function call that is both the strength and the weakness of this method. While it lets you cleanly segment your code, you also have a bunch of functions lying around. This isn’t a problem if you were going to use functions anyways, but they can be a mess if you have a lot of tiny ones lying around. If your functions sufficiently small, consider inlining them in the dictionary as lambda expressions.
As a final note, the above example doesn’t provide a default case in case nothing matches. You can make up for this by having the actual lookup be inside an if-else block, with the condition checking for the presence of the key in the dictionary (using the ‘<keyname> in <dictionary>’ idiom). But a more Pythonic way is to wrap it in a try/except block. If the option isn’t presence, a KeyError Exception will be raised which can then be caught and the default code executed. Be sure to check the Wikipedia entry and this blog post for more information.
Hi,
You gould also handle the missing default case by doing this instead:
options.get(num, lambda : None)()
Which will return None if num is not in options.
- Paddy.
Was in fact wondering how to avoid that if-else laddder. One of the practice programs I had done when first learning Python was a calculator – I believe that is in for an improvement.
The easiest solution would be to use ruby’s case
x = “bla”
case x
when /some_regex/
puts “blablabla”
when ‘a’,'b’,'c’
puts “yadda blablabla”
else
puts “some else blablabla”
end
The advantage would be to chain multiple if constructs together easily, without the cumbersome “or” chaining in the python example.
I think using functions via def is not a good way simply because a function should be somewhat “meaningful” in the first place, and not a substitute for no case/switch way control structures.
But then again using case/switch would violate python’s principle of using as few idioms as possible, so as to not confuse the average programmer.
PS: The blog form doesnt like my 2 space indent
You could need some html tags to denote example code in those formulars though
That python doesn’t have a switch statement is a huge black mark on the language and, frankly is an absolutely stupid decision. The example you used is too simplistic to point out how the ugly python hack fails.
switch(n) {
02. case 0:
03. printf(“You typed zero.\n”);
04. break;
05. case 1:
06. case 9:
07. printf(“n is a perfect square\n”);
08. break;
09. case 2:
10. printf(“n is an even number\n”);
11. case 3: printf(“n is this”);
12. case 5: printf(“n is also this”
13. case 7:printf(“n is too complex for silly python hacks”)
14. printf(“n is a prime number\n”);
15. break;
16. case 4:
17. printf(“n is a perfect square\n”);
18. case 6:
19. case 8:
20. printf(“n is an even number\n”);
21. break;
22. default:
23. printf(“Only single-digit numbers are allowed\n”);
24. break;
25.}
Good luck using dictionary with this.
Switch statements are more powerful than any ugly python hack.
“But then again using case/switch would violate python’s principle of using as few idioms as possible, so as to not confuse the average programmer.”
If switch statements are confusing, then that “programmer” should find a new field.
options = {}
options[0] = { ‘cmd’: printf, ‘args’: “You typed zero.\n” }
options[1] = { ‘cmd’: printf, ‘args’: “n is a perfect square\n” }
options[9] = { ‘cmd’: printf, ‘args’: “n is a perfect square\n” }
options[7] = { ‘cmd’: printn, ‘args’: None }
if options.has_key(n):
options[n]['cmd'](options[n]['args'])
else:
printf(“Only single-digit numbers are allowed\n”);
???????
I’m pretty sure this would work.
If not, convert n to a string and use ‘n’ vs n as key.
I do want a switch statement, and I don’t want to say that this solves all of the problems, but you can get exactly the same functionality you just mentioned from python with dictionaries.
It’s nowhere near ideal, I just find your example to be nonsensical. I also can’t think of a situation where this couldn’t be applied as a solution. You simply need to create a function for each case.
Perhaps you’re the one who is confused by an alien solution?
And yes, I realize this post is from 2009, but I felt the need to point this out for those who mistakenly agree with you.
Almost! You probably need to just add an asterisk, as in: (*options[n]['args'])
You do realize that NONE of the python “realizations”
you gave produces the same output as the C example,
right? The output of the C code will include *all*
options for a particular number (e.g. 2 is BOTH prime
AND even, 9 is a square AND odd, etc.), while your
attempts all cover only one of them.
Actually, if you compile (or read) the C code you will see that it does not.
Actually, if you understand the C code you will see that it does.
There is no break statement after the tests for 2 and 4.
Well, no, because it wouldn’t compile (syntax error on line 12)
@andraxin: I had honestly not realized that before. You’re quite right, and in retrospect, it’s quite a drawback to the Python implementations I presented. I think this post needs a followup
Pingback: Switch-case statement in Python revisited « The ByteBaker
Pingback: Powerful Python « The ByteBaker
Pingback: Revamping the ByteBaker series « The ByteBaker
if n == 0:
print “You typed zero.”
elif n in (1, 4, 9):
print “n is a perfect square”
elif n == 2:
print “n is an even number”
elif n in (3, 5, 7):
print “n is a prime number”
Hi,
If you’re searching extra-statement, as “switch”, I built a python module that extends Python. It’s called ESPY as Enhanced
Structure for Python and it’s available for both Python 2.x and Python 3.x.
For example, in this case, a switch statement could be performed by the following code:
“”"
macro switch(arg1):
….while True:
…….cont=False
……..val=%arg1%
……..socket case(arg2):
…………if val==%arg2% or cont:
…………….cont=True
…………….socket
……..socket else:
…………socket
……..break
“”"
that can be used like this:
“”"
a=3
switch(a):
….case(0):
……..print(“Zero”)
….case(1):
……..print(“Smaller than 2″):
……..break
….else:
……..print (“greater than 1″)
“”"
so espy translate it in Python as:
“”"
a=3
while True:
….cont=False
….if a==0 or cont:
……..cont=True
……..print (“Zero”)
….if a==1 or cont:
……..cont=True
……..print (“Smaller than 2″)
……..break
….print (“greater than 1″)
….break
“”"
You can find and test ESPY in this page:
elp.chronocv.fr/?lng=en
Eric LE PAPE
I was trying something similar, and it appears that you need to define the functions before you define the dictionary that will call those functions…
All time, I am searching about how to use switch in Ptyhon. So I can figured how to use with other function, because Python dont have switch function. Thx for sharing
You wouldn’t have to define other functions if you used lambda. So one of the dict entries could be
0: lambda : print “This was zero”,
now you don’t have to have extra functions and such. (and it reads relatively similarly to what you had already).
if you wanted to change a variable (or variables) you could define a single function to do so and then call it with a lambda, e.g.
def change_foundoption(option):
global found_option
found_option = option
and then just do the following
0: lambda: change_foundoption(“none”)
etc
which would, in fact, make it the equivalent of the ruby when/do statement, I believe
if n == 0:
print(“You typed zero.\n”)
elif n in [1, 9]:
print(“n is a perfect square\n”)
elif n in [2, 3, 5, 7]:
if n == 2: print(“n is an even number\n”)
if n == 3: print(“n is this”)
if n == 5: print(“n is also this”)
if n == 7: print(“n is too complex for silly python hacks”)
printf(“n is a prime number\n”)
elif n in [4, 6, 8]:
if n == 4: print(“n is a perfect square\n”)
print(“n is an even number\n”)
else:
printf(“Only single-digit numbers are allowed\n”)
I don’t think I’ve ever missed the switch statement, but I would not mind it either.
@Shrutarshi: Actually, the comment by @andraxin illustrates precisely *why* C style switch statements are not a desirable element in Python. The point of Python is that there should be no ambiguity in code. The intent should be crystal clear from code by inspection. The example by @regeya achieves the desired behaviour and in a manner which makes the intent clear immediately. Contrast this with the C code snippet, and only a very close inspection of the code would reveal the programmer’s intent.
C is designed for maximum flexibility — i.e. a hardware independent assembly language. Python is exactly the opposite: a language for programming clarity and ease.
Python should not duplicate every coding possibility in other languages, nor every ‘cleverness’ — most of these turn out to make code less immediately clear to a reader.
The suggested approch is very nice. Most people tend to think in switch case or if else construct. The suggested code uses Pythons design to make use of the Datastructure facility. Very nice approach.
Dont get locked into the reconstruction of C idioms, think in Problems and solutions. I had to fix a code once which had a if -else construction with 50 possible outcomes over 400 Lines. I had to draw a picture bevore I did understand what the writer wanted to accomplisch. A switch case would have eased the analyses a lot. (because it is always intended as a special case of if – else trees. Not only in C)
Have Fun!
Pingback: python - Simulating 'else' in dictionary switch statements | BlogoSfera
Here is another way to run the default value:
# define a default function
def defaultfunction():
print “this is the default value\n”
options.get(2, defaultfunction)()
# prints ‘n is an even number’
options.get(8, defaultfunction)()
# prints ‘this is the default value.’
thanks, it’s really helped me….