I want to write a function in python that returns different fixed values based on the value of an input index. In other languages I would use a switch or case statement, but python does not appear to have a switch statement. What are the recommended python solutions in this scenario?
You could use a dictionary:
def f(x):
return {
'a': 1,
'b': 2,
}[x]
I've always liked doing it this way
result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}[value](x)
If you are really just returning a predetermined, fixed value, you could create a dictionary with all possible input indexes as the keys, along with their corresponding values. Also, you might not really want a function to do this - unless you're computing the return value somehow.
Oh, and if you feel like doing something switch-like, see here.
In addition to the dictionary methods (which I really like, BTW), you can also use if-elif-else to obtain the switch/case/default functionality:
if x=='a':
# Do the thing
elif x=='b':
# Do the other thing
else:
# Do the default
This of course is not identical to switch/case - you cannot have fall-through as easily as leaving off the break; statement, but you can have a more complicated test. It's formatting is nicer than a series of nested ifs, even though functionally that's what it is closer to.
There's a pattern that I learned from Twisted Python code.
class SMTP:
def lookupMethod(self, command):
return getattr(self, 'do_' + command.upper(), None)
def do_HELO(self, rest):
return 'Howdy ' + rest
def do_QUIT(self, rest):
return 'Bye'
SMTP().lookupMethod('HELO')('foo.bar.com') # => 'Howdy foo.bar.com'
SMTP().lookupMethod('QUIT')('') # => 'Bye'
You can use it any time you need to dispatch on a token and execute extended piece of code. In a state machine you would have state_
methods, and dispatch on self.state
. This switch can be cleanly extended by inheriting from base class and defining your own do_
methods. Often times you won't even have do_
methods in the base class.
Edit: how exactly is that used
In case of SMTP you will receive HELO
from the wire. The relevant code (from twisted/mail/smtp.py
, modified for our case) looks like this
class SMTP:
# ...
def do_UNKNOWN(self, rest):
raise NotImplementedError, 'received unknown command'
def state_COMMAND(self, line):
line = line.strip()
parts = line.split(None, 1)
if parts:
method = self.lookupMethod(parts[0]) or self.do_UNKNOWN
if len(parts) == 2:
return method(parts[1])
else:
return method('')
else:
raise SyntaxError, 'bad syntax'
SMTP().state_COMMAND(' HELO foo.bar.com ') # => Howdy foo.bar.com
You'll receive ' HELO foo.bar.com '
(or you might get 'QUIT'
or 'RCPT TO: foo'
). This is tokenized into parts
as ['HELO', 'foo.bar.com']
. The actual method lookup name is taken from parts[0]
.
(The original method is also called state_COMMAND
, because it uses the same pattern to implement a state machine, i.e. getattr(self, 'state_' + self.mode)
)
The switch statement is just syntactical sugar which is probably why Python doesn't have it. You can use if else statements for this functionality easily.
Like Matthew Schinckel said, you can use if and elif and else.
It is also a simple matter to have "fall-through" capabilities like most switch statements. All you have to do is not use elif.
if x == 1:
# 1
if x == 2:
# fall-through
elif x == 3:
# not fall-through
I would just use if/elif/else statements. I think that it's good enough to replace the switch statement.
expanding on the "dict as switch" idea. if you want to use a default value for your switch:
def f(x):
try:
return {
'a': 1,
'b': 2,
}[x]
except KeyError:
return 'default'
If you'd like defaults you could use the dictionary "get" method:
def f(x):
return {
'a': 1,
'b': 2,
}.get(x, 9) # 9 is default if x not found
A true switch/case in Python is going to be more difficult than a dictionary method or if/elif/else methods because the simple versions do not support fall through. Another downfall of the if/elif/else method is the need for repeated comparisons. The C implementation of a switch/case has a performance benefit over if/else if/else in that only a single comparison is needed. The result of that comparison is used as an offset into a jump table (in the underlying asm generated). To mimicking the true functionality in Python would be a pain. Does any one have an implementation that would allow for fall through while only using a single comparison?
Python Cookbook has several recipes (implementations and corresponding discussions) for switch statement. Please visit the following links:
I have made a (relatively) flexible and re-usable solution for this. It can be found at GitHub as this gist. If the result of the switch function is callable, it is automatically called.
The solutions I use:
A combination of 2 of the solutions posted here, which is relatively easy to read and supports defaults.
result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}.get(whatToUse, lambda x: x - 22)(value)
where .get('c', lambda x: x - 22)(23) -> looks up "lambda x: x - 2" in the dict and uses it with x=23
.get('xxx', lambda x: x - 22)(44) -> doesn´t find it in the dict and uses the default "lambda x: x - 22" with x=44
Let's say you don't want to just return a value, but want to use methods that change something on an object. Using the approach stated here would be:
result = {
'a': obj.increment(x),
'b': obj.decrement(x)
}.get(value, obj.default(x))
What happens here is that python evaluates all methods in the dictionary. So even if your value is 'a', the object will get incremented and decremented by x.
Solution:
func, args = {
'a' : (obj.increment, (x,)),
'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))
result = func(*args)
So you get a list containing a function and its arguments. This way, only the function pointer and the argument list get returned, not evaluated. 'result' then evaluates the returned function call.