Python style switches. Showing several ways of making a 'switch' in python.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | #==================================================
# 1. Select non-function values:
#==================================================
location = 'myHome'
fileLocation = {'myHome' :path1,
'myOffice' :path2,
'somewhere':path3}[location]
#==================================================
# 2. Select functions:
#==================================================
functionName = input('Enter a function name')
eval('%s()'%functionName)
#==================================================
# 3. Select values with 'range comparisons':
#==================================================
#
# Say, we have a range of values, like: [0,1,2,3]. You want to get a
# specific value when x falls into a specific range:
#
# x<0 : 'return None'
# 0<=x<1: 'return 1'
# 1<=x<2: 'return 2'
# 2<=x<3: 'return 3'
# 3<=x : 'return None'
#
# It is eazy to construct a switch by simply making the above rules
# into a dictionary:
selector={
x<0 : 'return None',
0<=x<1 : 'return 1',
1<=x<2 : 'return 2',
2<=x<3 : 'return 3',
3<=x : 'return None'
}[1]
# During the construction of the selector, any given x will turn the
# selector into a 2-element dictionary:
selector={
0 : 'return None',
1 : #(return something you want),
}[1]
# This is very useful in places where a selection is to be made upon any
# true/false decision. One more example:
selector={
type(x)==str : "it's a str",
type(x)==tuple: "it's a tuple",
type(x)==dict : "it's a dict"
} [1]
#==================================================
# 4. Select functions with 'range comparisons':
#==================================================
#
# You want to execute a specific function when x falls into a specific range:
functionName={
x<0 : setup,
0<=x<1: loadFiles,
1<=x<2: importModules
}[1]
functionName()
#==================================================
# 5. and/or style
#==================================================
#
# x = a and 'a' or None
#
# is same as:
#
# if a: x = 'a'
# else: x = None
#
# More example: a switch in Basic:
#
Select Case x
Case x<0 : y = -1
Case 0<=x<1 : y = 0
Case 1<=x<2 : y = 1
Case 2<=x<3 : y = 2
Case Else : y = 'n/a'
End Select
#
# in Python
#
y = ( (x<0) and -1 ) or \
( (0<=x<1) and 0 ) or \
( (1<=x<2) and 1 ) or \
( (2<=x<3) and 2 ) or 'n/a'
|
Be careful when selecting functions using the 'dictionary-based' switches :
If it is constructed this way:
aFunction={ x<0 : setup(), 0<=x<1: loadFiles(), 1<=x<2: importModules() }[1]
then all 3 functions will be executed during the construction of aFunction.
Tags: shortcuts
A switch example in a 'Rock, Scissors, Paper' game. http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/181064
A switch example in a 'Rock, Scissors, Paper' game. http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/181064
No need to use eval. You give the need not to call each function while constructing the dictionary as the reason for using strings containing the function names and eval(). However functions are first-class objects that you can pass around - they only get called when you use the () operator on them. Thus you can instead write:
(For Python 2.2.1 and later it would also probably be more readable to use True rather than 1 as the index for the dictionary.)
Thx for the input. The code was modified accordingly.
You're welcome ... ... but just a further small point of style: since the values in the dictionary are no longer the names of functions but references to the functions themselves, perhaps 'functionName' is now a less appropriate variable name - hence my suggestion of 'function'.
Relatively high penalty for doing range comparisons. Compared to an if/elif equivalent, there's quite a bit of overhead involved because, in addition to the construction of the dictionary itself, all of the condition tests are executed during the process.
Performance testing using a large number of random input values indicate than the technique is about 66-70% slower than using a simple if/elif series of logical expressions.
Whether or not the speed is important, of course, depends on many other factors and would need to be traded-off against the arguably cleaner-looking syntax.
As for the other proposed uses (selecting function and non-fuction values) -- isn't that exactly the purpose of dictionary/mapping objects? Although using temporarily constructed ones in this manor is clever, I suppose.
Some discussions about python switches: http://simon.incutio.com/archive/2004/05/07/switch#comments
Some discussions about python switches: http://simon.incutio.com/archive/2004/05/07/switch#comments
dangerous in and/or style! In and/or style, at the position between 'and' and 'or', you cannot use 0 or any other values which python consider as False in boolean expression.
In your example:
y will get the wrong 'n/a' when x equals 0.5, not 0, the correct answer.
6. Dispatch style. As long as the switch parameter contains no characters that are invalid in an identifier:
...with a separate method for each option:
A functional approach: Since I am a fan of functional style, I always have the following cond function handy:
The parenthesis allows us to write stuff like
which is IMHO quite readable. And in this example, if x>0, it does not even evaluate the other branch. Thanks, lambda!
Some other approaches and discussions:
Replacements for switch statement in Python?
http://stackoverflow.com/questions/60208/replacements-for-switch-statement-in-python/34639742#34639742