Switch statements in Python
Python doesn’t support a native switch statement. I’ve found myself using the following coding idiom instead recently which seems to work pretty well:
{'option1': function1,
'option2': function2,
'option3': function3,
'option4': function4}[value]()
This works with lambdas as well, for inline calculations. Here’s a switch statement that assigns to a variable in PHP:
switch ($value) {
case 'a':
$result = $x * 5;
break;
case 'b':
$result = $x + 7;
break;
case 'c':
$result = $x - 2;
break;
}
And here’s the equivalent code in Python:
result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}[value](x)
Yeah, the lack of switch() through me for a bit of a loop when first starting in Python, but all said and done, I have found I don't miss it all that much. Especially when I can use a dictionary map in it's place if I have to. The syntax is a little weird if you're coming from a c-ish background, but one must admit that it's much more concise and not as overly verbose as a lot of languages are with switch().
Dave Giffin - 7th May 2004 04:18 - #
What about a default entry, if
valueis not in the hash?Dominic Mitchell - 7th May 2004 07:42 - #
That's where things get a little messy. You can do a default clause by wrapping the whole code chunk in a try/catch handler, but that can lead to nasty problems if one of the functions called from within the block throws an uncaught KeyError exception of its own (those can be a real sod to debug if you're catching them accidentally at a higher level). The code ends up looking like this:
Simon Willison - 7th May 2004 08:10 - #
{'option1': function1, 'option2': function2, 'option3': function3, 'option4': function4, }.get(value, defaultfunction)()Josu Oyanguren - 7th May 2004 08:28 - #
Roberto - 7th May 2004 08:56 - #
Creating and throwing away a dictionary each time makes me a bit nervous about efficiency. But maybe the compiler recognizes constant dictionaries and optimizes their creation?
There is also the variations on
where your functions are now named
function_option1,function_option2, and so on.Damian Cugley - 7th May 2004 10:56 - #
In situations where you could use a C-style
switchstatement (ie where you're just relying on imperative effects of the code, and if you want a result you have to assign it to a variable) you can use a chain ofifandelifstatements, like:This seems as concise as
switchto me, and also has the advantage that you can use other tests as well as equality. It also allows you to have a default response if the value isn't one of the ones tested; with a dictionary this would throw an exception, which is maybe what you'd want anyway. A possible disadvantage is that if the value you're switching on is an expression, it'll be evaluated more than once (and if it has side-effects, they'll occur more than once) so if this is the case you'll want to assign it to a variable first.The dictionary method is nice where you want it to return a result directly, though, as in your example with the lambdas. For the best of both worlds, it would be good to have something like Lisp's
cond.Andy Smith - 7th May 2004 15:18 - #
Lambda inherits the environment-- it would probably be better to not pass any arguments to the lambda function:
result = { 'a': lambda : x * 5, 'b': lambda : x + 7, 'c': lambda : x - 2 }[value]()That said, I like if/elif/else better.
John Doty - 7th May 2004 17:11 - #
var x = 10 ; var y = 20 ; var operator = "*" var result = { "+" : function(x, y) { return x + y } , "-" : function(x, y) { return x - y } , "*" : function(x, y) { return x * y } , "/" : function(x, y) { return x / y } }[operator](x, y) ;Tom Trenka - 7th May 2004 18:41 - #
John-- you must be old school. Who needs a stinkin' switch when we've got GOTO?
I'm thinkin' fortran here. :)
Jeremy Dunck - 8th May 2004 02:55 - #
Lach - 8th May 2004 02:59 - #
Simon Willison - 8th May 2004 04:31 - #
thisdirectly. Does Python have similar problems?)liorean - 8th May 2004 15:08 - #
switch = { '1': (lambda, False), '2': (lamda2, False), '3': (lamda3, True), '4': (lambda4, False) } fall = False for key, val in switch: if x == key or fall: val[0]() fall = val[1]perhaps? Still, that's nowhere near as compact as the other examples.
Lach - 9th May 2004 02:00 - #
Sorry, but I hate all of them! Verbosity isn't a inherit problem of a programming language... It is an advantage when used correctly. In practice simple syntaxes don't really save much time for the programmer, but they often hard to read and document and easy to mess up the syntax. (Hence, more bugs.) Of couse, the programmer could add stuff which isn't needed (var type often isn't necessary, inconsistant syntax or synonyms that have different functions, etcetera) and write poorly written verbose code. However, simple code is bad too.
I even dislike the traditional syntax of switch statements. They come from a time of goto and labels. Switch statments must be read linearly and completely to understand how the program flows (i.e. break keywords in unsusal places). This leads to spaghetti code. All the examples here are worse (for traditional romanic/germanic speaking people) with the possibilities before the question!
Besides, I gather than most compilers can easily optimize "switch-like" if-then-elseif blocks. Let it go.
Jimmy C. - 9th May 2004 05:30 - #
As one comment above mentions, switch statements in most languages are also quite limited in their tests, though I was impressed by Damian Conway's ability to add a switch statement to perl, which offered most types of tests that you might want in a modern language, from regular expressions to list and dictionary membership, without modifying the interpreter. See Switch.pm for details.
James Walden - 9th May 2004 21:24 - #
$str = "abc"; switch( $str ) { case "aaa": { echo "\$str = 'aaa'"; break; } case substr( $str, -1 )=="c": { echo "\$str ends with a 'c'"; break; } default: { echo "default"; break; } }You can also use some 'complex' things like the in_array function or whatever you want.NB: I like to put some {} to clearly delimit the blocks of statements of the various cases
Mathieu 'P01' HENRI - 10th May 2004 00:40 - #
As a general rule, I don't bother going through simulating a switch unless I have a large list of possible options, say a day of the month or some such. In cases where there are only likely to be 2 to 10 options, I usually just use an if, elif, else chain. One benefit is that it allows a much more in depth level of testing. And all said and done, I haven't noticed any serious (or even mild) performance hit doing this. Now, if the test would need to be against say, 1 through 31 or 1 through Z, well, then I would roll the dictionary, as it would likely be a performance improvement, less overall code and clearer to read later, but otherwise, why bother for most things?
Python is an elegant language compared to most others. I don't often find myself missing the switch() function very often. I just keep coding and when I hit something I would have used a switch() for in another language, it seems it's just not needed in Python.
Dave Giffin - 10th May 2004 15:01 - #
The answer that I have received in the past is that switches are rarely needed in a language that supports polymorphism. The switch statement is a procedural crutch for lack of object differentiation. I the example above each "x" would be a different subclass, and the proper function would be called based on the subclass's definition of the function.
There's a common refactoring based on this: ReplaceConditionalWithPolymorphism.
Rob - 10th May 2004 19:54 - #
Using polymorphism to switch seems a bit overboard. Maybe a happy medium is creating methods for each case rather than subclasses. It's really handy in Ruby.
For example, a common use of a case statement is for command-line actions. So, let's say you're building a command-line time clock tool. With 'in', 'out' and 'edit' commands.
def in_action( client_name ) .. db call to handle clocking in .. end def out_action() .. db call to clock out .. end def edit_action( time_entry_id, client_name ) .. db call to update the entry .. endThen, in lieu of my original case statement:
cmd, *opts = ARGV if respond_to? cmd method( cmd ).call( *opts ) else default_action endI feel like case statements are four-way stops. They direct traffic in a simple fashion. So, with methods, you can build traffic lights and crossing guards in easily.
Like with the above, I could add explanatory text for each option by adding explain methods:
The example's sorta contrived, but I think it's another alternative worth considering.
_why - 11th May 2004 00:56 - #
If the vertical length of the code is problem, the if/elif syntax can be compressed a little:
I quite like this syntax, though having the suite on the same line as the test may offend your sense of style.
Alan Green - 11th May 2004 01:42 - #
I find myself using the following idiom, which allows me to do tricky C
switchthings like fall through from one case to another:for value in [value]: if value == 'a': result = x * 5 break if value == 'b': result = x + 7 break if value == 'c': result = x - 2 break # default result = x breakOK, that first line is a little contrived, but there's typically something more complex than
[value]up there on the first line. I'm often testing the result of a more complex expression (likeswitch(expr)in C) so it's useful to capture that computation once.It doesn't quite fall through like C
switch(the test you fall into still has to succeed). But, hey, it does use thebreakkeyword.Dan Sandler - 11th May 2004 14:10 - #
Joe - 14th May 2004 21:26 - #
(cond1 and do1()) or (cond2 and do2()) ...?????
and what do you do if do1() return a false value? so you have to write (cond1 and do1() or True) or ....
KISS and use if
what about:
iFooBar = ......
if iFooBar < 0:
do
else if iFooBar==1:
do
else if iFooBar==2:
do
else :
do
#
where is the different to switch ?
break? nobody like break
Floirian - 18th May 2004 16:04 - #
Tushar - 14th June 2004 18:29 - #
I really have to agree with Jimmy C here, there are some neat tricks here, but I'd hate to be the one maintaining your code. I think it's a good idea to explore them though, because this kind of exploration makes sure you really know the language/compiler/interpreter you're dealing with.
For those wondering about how to simulate fall-through case statements: I think it's interesting to note that the C# designers chose to explicity disallow fall-through in switch statements. Every code block associated with a case label in a C# switch must end with a break or a goto:
switch(var) { case 1: // ... break; case 2: // ... goto case 3; // simulate fall-through case 3: // ... default: // compiler error here -- fall-through == bad! // ... }I believe this is to allow people to re-order the case clauses willy-nilly, and to make sure you don't accidentally generate fall-through code by leaving out a break. Another case where verbosity is required for readability (and I like it..).
Stewart Johnson - 2nd December 2004 19:23 - #
aguvaza_a@yahoo.com - 21st February 2005 12:35 - #
tafa - 30th May 2005 18:51 - #
switch (value) { case 'A': case 'B': case 'C': doSomething(); break; case 'D': doSomethingBefore(); goto 'A'; break; default: break; }Julio César Carrascal - 17th August 2005 04:47 - #
MikeFM - 21st August 2005 01:55 - #
Do you realise that:
test ? true : falseis equivalent to the following python code?
test and true or falsesulla - 28th August 2005 08:52 - #
Straw Dogs - 6th September 2005 23:10 - #
kim - 9th October 2005 16:46 - #
Do you realise that:
test ? true : falseis equivalent to the following python code?
test and true or falseThis doesn't work out very well if your then-value happens to be considered false (eg empty strings or lists; or None):
>>> condition = True>>> thenval = ''>>> elseval = 'oops'>>> print condition and thenval or elsevaloopsedward - 14th October 2005 07:07 - #
#listing : simple_menu.py ************************** import sys def menuLoop(prompt, menu, data): while 1: cmdstr = raw_input(prompt) cmd = cmdstr[0] result = True if len(cmdstr) > 0: if menu.has_key(cmd): result = menu[cmd](cmdstr, data) else: print "Unknown command, type h for help." if not result: sys.exit(0); #listing:main_menu.py ************************** from simple_menu import * def mainMenu_DisplayHelp(cmd, data): print "Help!" print "you type %s" %cmd return True; def mainMenu_Exit(cmd, data): return False; def mainMenu_DisplayExports(cmd, data): exports = data["db"].getExports() print exports return True; def mainMenu(data): menuLoop("main :", {'h': mainMenu_DisplayHelp, '?': mainMenu_DisplayHelp, 'e': mainMenu_DisplayExports, 'x': mainMenu_Exit}, data) #listing main.py: ************************** import main_menu db = create_the_db() main_menu.mainMenu({"db":db})David Haler - 19th December 2005 22:44 - #