Replacements for switch statement in Python?
我想用python编写一个函数,它根据输入索引的值返回不同的固定值。 在其他语言中,我会使用switch或case语句,但python似乎没有switch语句。在这个场景中,推荐的python解决方案是什么?
你可以用字典:
1 2 3 4 5
| def f(x):
return {
'a': 1,
'b': 2,
}[x] |
如果您希望使用默认值,可以使用字典get(key[, default])方法:
1 2 3 4 5
| def f(x):
return {
'a': 1,
'b': 2
}.get(x, 9) # 9 is default if x not found |
我一直喜欢这样做
1 2 3 4 5
| result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}[value](x) |
从这里
除了字典方法(我真的很喜欢,btw),您还可以使用if elif else获得开关/案例/默认功能:
1 2 3 4 5 6 7 8 9 10
| if x == 'a':
# Do the thing
elif x == 'b':
# Do the other thing
if x in 'bc':
# Fall-through by not using elif, but now the default case includes case 'a'!
elif x in 'xyz':
# Do yet another thing
else:
# Do the default |
当然,这和switch/case不一样——您不可能像不使用break;语句那样容易失败,但您可以进行更复杂的测试。它的格式比一系列嵌套的ifs要好,即使在功能上这是它更接近的。
我最喜欢的python开关/外壳配方是:
1 2
| choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default') |
简单而简短的场景。 与11+行C代码比较:
1 2 3 4 5 6 7 8 9 10 11 12
| // C Language version of a simple 'switch/case'.
switch( key )
{
case 'a' :
result = 1;
break;
case 'b' :
result = 2;
break;
default :
result = -1;
} |
甚至可以通过使用元组来分配多个变量:
1 2
| choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3')) |
1 2 3 4 5 6 7 8
| class switch(object):
value = None
def __new__(class_, value):
class_.value = value
return True
def case(*args):
return any((arg == switch.value for arg in args)) |
用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| while switch(n):
if case(0):
print"You typed zero."
break
if case(1, 4, 9):
print"n is a perfect square."
break
if case(2):
print"n is an even number."
if case(2, 3, 5, 7):
print"n is a prime number."
break
if case(6, 8):
print"n is an even number."
break
print"Only single-digit numbers are allowed."
break |
测验:
1 2 3 4 5 6 7
| n = 2
#Result:
#n is an even number.
#n is a prime number.
n = 11
#Result:
#Only single-digit numbers are allowed. |
我从扭曲的Python代码中学习了一种模式。
1 2 3 4 5 6 7 8 9 10
| 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' |
您可以随时使用它来分派一个令牌并执行扩展的代码段。在状态机中,您可以使用state_方法,并在self.state上调度。通过从基类继承并定义自己的do_方法,可以清晰地扩展此开关。通常在基类中甚至没有do_方法。 编辑:具体使用方法 如果是smtp,您将收到来自电线的HELO。相关代码(来自twisted/mail/smtp.py,针对我们的案例进行了修改)如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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 |
你会收到' HELO foo.bar.com '(或者你可能会收到'QUIT'或'RCPT TO: foo')。这被标记为parts作为['HELO', 'foo.bar.com']。实际方法查找名称取自parts[0]。 (原始方法也称为state_COMMAND,因为它使用相同的模式来实现状态机,即getattr(self, 'state_' + self.mode))
我最喜欢的是一个非常好的食谱。你会很喜欢的。它是我见过的最接近实际的switch case语句的语句,特别是在特性中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class switch(object):
def __init__(self, value):
self.value = value
self.fall = False
def __iter__(self):
"""Return the match method once, then stop"""
yield self.match
raise StopIteration
def match(self, *args):
"""Indicate whether or not to enter a case suite"""
if self.fall or not args:
return True
elif self.value in args: # changed for v1.5, see below
self.fall = True
return True
else:
return False |
下面是一个例子:
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
| # The following example is pretty much the exact use-case of a dictionary,
# but is included for its simplicity. Note that you can include statements
# in each suite.
v = 'ten'
for case in switch(v):
if case('one'):
print 1
break
if case('two'):
print 2
break
if case('ten'):
print 10
break
if case('eleven'):
print 11
break
if case(): # default, could also just omit condition or 'if True'
print"something else!"
# No need to break here, it'll stop anyway
# break is used here to look as much like the real thing as possible, but
# elif is generally just as good and more concise.
# Empty suites are considered syntax errors, so intentional fall-throughs
# should contain 'pass'
c = 'z'
for case in switch(c):
if case('a'): pass # only necessary if the rest of the suite is empty
if case('b'): pass
# ...
if case('y'): pass
if case('z'):
print"c is lowercase!"
break
if case('A'): pass
# ...
if case('Z'):
print"c is uppercase!"
break
if case(): # default
print"I dunno what c was!"
# As suggested by Pierre Quentel, you can even expand upon the
# functionality of the classic 'case' statement by matching multiple
# cases in a single shot. This greatly benefits operations such as the
# uppercase/lowercase example above:
import string
c = 'A'
for case in switch(c):
if case(*string.lowercase): # note the * for unpacking as arguments
print"c is lowercase!"
break
if case(*string.uppercase):
print"c is uppercase!"
break
if case('!', '?', '.'): # normal argument passing style also applies
print"c is a sentence terminator!"
break
if case(): # default
print"I dunno what c was!" |
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
| class Switch:
def __init__(self, value):
self.value = value
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
return False # Allows a traceback to occur
def __call__(self, *values):
return self.value in values
from datetime import datetime
with Switch(datetime.today().weekday()) as case:
if case(0):
# Basic usage of switch
print("I hate mondays so much.")
# Note there is no break needed here
elif case(1,2):
# This switch also supports multiple conditions (in one line)
print("When is the weekend going to be here?")
elif case(3,4):
print("The weekend is near.")
else:
# Default would occur here
print("Let's go have fun!") # Didn't use case for example purposes |
假设您不希望只返回一个值,而是希望使用更改对象上某些内容的方法。使用这里所述的方法将是:
1 2 3 4
| result = {
'a': obj.increment(x),
'b': obj.decrement(x)
}.get(value, obj.default(x)) |
这里所发生的是,python计算字典中的所有方法。因此,即使您的值是"a",对象也将递增和递减x。 解决方案:
1 2 3 4 5 6
| func, args = {
'a' : (obj.increment, (x,)),
'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))
result = func(*args) |
所以你得到一个包含函数及其参数的列表。这样,只返回函数指针和参数列表,而不计算。'然后,result'计算返回的函数调用。
我只想把我的两分钱放在这里。python中没有case/switch语句的原因是因为python遵循"只有一种正确的方法可以做某事"的原则。很明显,您可以想出各种方法来重新创建开关/案例功能,但实现这一点的方法是if/elif构造。工业工程
1 2 3 4 5 6 7 8
| if something:
return"first thing"
elif somethingelse:
return"second thing"
elif yetanotherthing:
return"third thing"
else:
return"default thing" |
我只是觉得8号种子应该在这里点头。关于python的一个美丽之处就是它的简单和优雅。这在很大程度上源于我们在PEP 8中提出的原则,包括"只有一种正确的方法可以做某事"。
扩展"dict as switch"的概念。如果要对交换机使用默认值:
1 2 3 4 5 6 7 8
| def f(x):
try:
return {
'a': 1,
'b': 2,
}[x]
except KeyError:
return 'default' |
如果您有一个复杂的事例块,可以考虑使用函数字典查找表… 如果您以前没有这样做,那么最好进入调试器并查看字典如何查找每个函数。 注意:不要在事例/字典查找中使用"()",否则在创建词典/事例块时,它将调用每个函数。记住这一点,因为您只希望使用哈希样式查找调用每个函数一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def first_case():
print"first"
def second_case():
print"second"
def third_case():
print"third"
mycase = {
'first': first_case, #do not use ()
'second': second_case, #do not use ()
'third': third_case #do not use ()
}
myfunc = mycase['first']
myfunc() |
如果您要搜索额外的语句,作为"switch",我构建了一个扩展python的python模块。它被称为ESpy,是"针对python的增强结构",它同时适用于python 2.x和python 3.x。 例如,在这种情况下,可以使用以下代码执行switch语句:
1 2 3 4 5 6 7 8 9 10 11
| macro switch(arg1):
while True:
cont=False
val=%arg1%
socket case(arg2):
if val==%arg2% or cont:
cont=True
socket
socket else:
socket
break |
可以这样使用:
1 2 3 4 5 6 7 8 9
| a=3
switch(a):
case(0):
print("Zero")
case(1):
print("Smaller than 2"):
break
else:
print ("greater than 1") |
所以espy用python将其翻译为:
1 2 3 4 5 6 7 8 9 10 11 12
| 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 |
我在谷歌搜索中找不到简单的答案。但我还是想出来了。这真的很简单。决定张贴它,也许可以防止其他人的头上少一些划痕。关键是简单的"in"和元组。下面是switch语句与fall through的行为,包括随机fall through。
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
| l = ['Dog', 'Cat', 'Bird', 'Bigfoot',
'Dragonfly', 'Snake', 'Bat', 'Loch Ness Monster']
for x in l:
if x in ('Dog', 'Cat'):
x +=" has four legs"
elif x in ('Bat', 'Bird', 'Dragonfly'):
x +=" has wings."
elif x in ('Snake',):
x +=" has a forked tongue."
else:
x +=" is a big mystery by default."
print(x)
print()
for x in range(10):
if x in (0, 1):
x ="Values 0 and 1 caught here."
elif x in (2,):
x ="Value 2 caught here."
elif x in (3, 7, 8):
x ="Values 3, 7, 8 caught here."
elif x in (4, 6):
x ="Values 4 and 6 caught here"
else:
x ="Values 5 and 9 caught in default."
print(x) |
提供:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Dog has four legs
Cat has four legs
Bird has wings.
Bigfoot is a big mystery by default.
Dragonfly has wings.
Snake has a forked tongue.
Bat has wings.
Loch Ness Monster is a big mystery by default.
Values 0 and 1 caught here.
Values 0 and 1 caught here.
Value 2 caught here.
Values 3, 7, 8 caught here.
Values 4 and 6 caught here
Values 5 and 9 caught in default.
Values 4 and 6 caught here
Values 3, 7, 8 caught here.
Values 3, 7, 8 caught here.
Values 5 and 9 caught in default. |
我发现一种常见的开关结构:
1 2 3 4
| switch ...parameter...
case p1: v1; break;
case p2: v2; break;
default: v3; |
可以用python表示如下:
1
| (lambda x: v1 if p1(x) else v2 if p2(x) else v3) |
或以更清晰的方式格式化:
1 2 3 4
| (lambda x:
v1 if p1(x) else
v2 if p2(x) else
v3) |
python版本不是一个语句,而是一个表达式,它的计算结果是一个值。
我使用的解决方案: 这里发布的两个解决方案的组合,相对容易读取,并且支持默认值。
1 2 3 4 5
| result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}.get(whatToUse, lambda x: x - 22)(value) |
哪里
1
| .get('c', lambda x: x - 22)(23) |
在口述中查找"lambda x: x - 2",与x=23一起使用。
1
| .get('xxx', lambda x: x - 22)(44) |
在dict中找不到它,并将默认的"lambda x: x - 22"与x=44一起使用。
这里的大多数答案都很老,尤其是那些被接受的答案,所以似乎值得更新。 首先,官方的python常见问题解答涵盖了这一点,并建议简单案例使用elif链,较大或更复杂的案例使用dict。它还建议在某些情况下使用一组visit_方法(许多服务器框架使用的样式):
1 2 3 4
| def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method() |
FAQ还提到了PEP 275,它是为了让一位官员一次性决定添加C样式的切换语句。但这个PEP实际上被推迟到了Python3,而它只是作为一个单独的提议被正式拒绝了,即PEP 3103。当然,答案是否定的,但是如果你对原因或历史感兴趣的话,这两个政治公众人物会链接到其他信息。 多次出现的一件事(可以在PEP 275中看到,即使它被删去作为一个实际的建议)是,如果你真的为8行代码来处理4个案例而烦恼,而不是你在C或bash中所用的6行代码,你总是可以这样写:
1 2 3 4
| if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place') |
PEP8并不鼓励这样做,但它可读性强,不太单一。 在PEP 3103被拒绝的十多年里,C风格的case-statements问题,甚至是Go中功能稍强的版本,都被认为是死路一条;每当有人提出关于python的想法或-dev,他们都会提到旧的决定。 然而,完整的ML样式模式匹配的思想每隔几年就出现一次,特别是像Swift和Rust这样的语言已经采用了它。问题是,没有代数数据类型的模式匹配很难得到更多的使用。尽管guido对此表示同情,但没有人提出一个适合python的建议。(例如,你可以阅读我的2014年战略报告。)这可能会随着3.7中的dataclass和一些零星的建议而改变,这些建议要求更强大的enum来处理总和类型,或者针对不同类型的声明和地方约束提出各种建议(如pep 3150,或者目前正在讨论的一组建议-想法)。但到目前为止,还没有。 有时也有Perl6风格匹配的建议,从elif到regex到单调度类型切换,基本上都是混乱的。
1 2 3 4
| def f(x):
dictionary = {'a':1, 'b':2, 'c':3}
return dictionary.get(x,'Not Found')
##Returns the value for the letter x;returns 'Not Found' if x isn't a key in the dictionary |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| # simple case alternative
some_value = 5.0
# this while loop block simulates a case block
# case
while True:
# case 1
if some_value > 5:
print ('Greater than five')
break
# case 2
if some_value == 5:
print ('Equal to five')
break
# else case 3
print ( 'Must be less than 5')
break |
我喜欢马克·比斯的回答 由于x变量必须使用两次,所以我将lambda函数修改为无参数。 我得和results[value](value)一起跑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| In [2]: result = {
...: 'a': lambda x: 'A',
...: 'b': lambda x: 'B',
...: 'c': lambda x: 'C'
...: }
...: result['a']('a')
...:
Out[2]: 'A'
In [3]: result = {
...: 'a': lambda : 'A',
...: 'b': lambda : 'B',
...: 'c': lambda : 'C',
...: None: lambda : 'Nothing else matters'
...: }
...: result['a']()
...:
Out[3]: 'A' |
编辑:我注意到我可以在字典中使用None类型。所以这将模仿switch ; case else。
1 2 3 4
| def f(x):
return 1 if x == 'a' else\
2 if x in 'bcd' else\
0 #default |
简短易读,有一个默认值,在条件和返回值中都支持表达式。 但是,它比使用字典的解决方案效率低。例如,在返回默认值之前,python必须扫描所有条件。
简单,不测试;每个条件都是独立计算的:没有失败,但是所有情况都是计算的(尽管要打开的表达式只计算一次),除非有break语句。例如,
1 2 3 4 5 6 7 8 9 10 11 12
| for case in [expression]:
if case == 1:
print(end='Was 1. ')
if case == 2:
print(end='Was 2. ')
break
if case in (1, 2):
print(end='Was 1 or 2. ')
print(end='Was something. ') |
打印Was 1. Was 1 or 2. Was something.(该死!为什么在内联代码块中不能有尾随空格?)如果expression对1进行评估,如果expression对2进行评估,则Was 2.对2进行评估,如果expression对其他东西进行评估,则Was something.对expression进行评估。
我倾向于使用字典的解决方案是:
1 2 3 4 5 6 7 8 9 10 11 12
| def decision_time( key, *args, **kwargs):
def action1()
"""This function is a closure - and has access to all the arguments"""
pass
def action2()
"""This function is a closure - and has access to all the arguments"""
pass
def action3()
"""This function is a closure - and has access to all the arguments"""
pass
return {1:action1, 2:action2, 3:action3}.get(key,default)() |
这样做的好处是,它不会每次都尝试评估函数,您只需确保外部函数获取内部函数所需的所有信息。
运行函数的解决方案:
1 2 3 4 5 6
| result = {
'case1': foo1,
'case2': foo2,
'case3': foo3,
'default': default,
}.get(option)() |
其中foo1()、foo2()、foo3()和default()是函数
我认为最好的方法是使用Python语言习语来保持代码的可测试性。如前面的答案所示,我使用字典来利用Python结构和语言,并以不同的方法隔离"case"代码。下面有一个类,但是您可以直接使用模块、全局和函数。类具有可以用隔离测试的方法。根据您的需要,您也可以使用静态方法和属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class ChoiceManager:
def __init__(self):
self.__choice_table = \
{
"CHOICE1" : self.my_func1,
"CHOICE2" : self.my_func2,
}
def my_func1(self, data):
pass
def my_func2(self, data):
pass
def process(self, case, data):
return self.__choice_table[case](data)
ChoiceManager().process("CHOICE1", my_data) |
利用这个方法,还可以使用类作为"uu choice_table"的键。这样,您就可以避免ISInstance滥用,并保持所有的清洁和可测试性。 假设您必须处理来自网络或MQ的大量消息或数据包。每个包都有自己的结构和管理代码(以通用方式)。使用上述代码,可以执行如下操作:
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
| class PacketManager:
def __init__(self):
self.__choice_table = \
{
ControlMessage : self.my_func1,
DiagnosticMessage : self.my_func2,
}
def my_func1(self, data):
# process the control message here
pass
def my_func2(self, data):
# process the diagnostic message here
pass
def process(self, pkt):
return self.__choice_table[pkt.__class__](pkt)
pkt = GetMyPacketFromNet()
PacketManager().process(pkt)
# isolated test or isolated usage example
def test_control_packet():
p = ControlMessage()
PacketManager().my_func1(p) |
因此,复杂性不是在代码流中扩散的,而是在代码结构中呈现的。
您可以使用发送的dict:
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
| #!/usr/bin/env python
def case1():
print("This is case 1")
def case2():
print("This is case 2")
def case3():
print("This is case 3")
token_dict = {
"case1" : case1,
"case2" : case2,
"case3" : case3,
}
def main():
cases = ("case1","case3","case2","case1")
for case in cases:
token_dict[case]()
if __name__ == '__main__':
main() |
输出:
1 2 3 4
| This is case 1
This is case 3
This is case 2
This is case 1 |
读完答案后我很困惑,但这一切都清楚了:
1 2 3 4 5 6 7
| def numbers_to_strings(argument):
switcher = {
0:"zero",
1:"one",
2:"two",
}
return switcher.get(argument,"nothing") |
此代码类似于:
1 2 3 4 5 6 7 8 9 10 11 12
| function(argument){
switch(argument) {
case 0:
return"zero";
case 1:
return"one";
case 2:
return"two";
default:
return"nothing";
}
} |
有关字典映射到函数的更多信息,请查看源代码。
扩展Greg Hewgill的答案-我们可以使用装饰器封装字典解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def case(callable):
"""switch-case decorator"""
class case_class(object):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
def do_call(self):
return callable(*self.args, **self.kwargs)
return case_class
def switch(key, cases, default=None):
"""switch-statement"""
ret = None
try:
ret = case[key].do_call()
except KeyError:
if default:
ret = default.do_call()
finally:
return ret |
然后可以与@case装饰器一起使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @case
def case_1(arg1):
print 'case_1: ', arg1
@case
def case_2(arg1, arg2):
print 'case_2'
return arg1, arg2
@case
def default_case(arg1, arg2, arg3):
print 'default_case: ', arg1, arg2, arg3
ret = switch(somearg, {
1: case_1('somestring'),
2: case_2(13, 42)
}, default_case(123, 'astring', 3.14))
print ret |
好消息是这已经在Neopyswitch模块中完成了。只需使用PIP安装:
如果您不担心在case套件中丢失语法突出显示,可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13
| exec {
1:"""
print ('one')
""",
2:"""
print ('two')
""",
3:"""
print ('three')
""",
}.get(value,"""
print ('None')
""") |
其中,value为数值。在c中,这是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| switch (value) {
case 1:
printf("one");
break;
case 2:
printf("two");
break;
case 3:
printf("three");
break;
default:
printf("None");
break;
} |
我们还可以创建一个助手函数来执行此操作:
1 2
| def switch(value, cases, default):
exec cases.get(value, default) |
因此,我们可以像这样将其用于示例1、2和3:
1 2 3 4 5 6 7 8 9 10 11 12 13
| switch(value, {
1:"""
print ('one')
""",
2:"""
print ('two')
""",
3:"""
print ('three')
""",
},"""
print ('None')
""") |
定义:
1 2 3
| def switch1(value, options):
if value in options:
options[value]() |
允许您使用相当简单的语法,将案例捆绑到地图中:
1 2 3 4 5 6 7 8
| def sample1(x):
local = 'betty'
switch1(x, {
'a': lambda: print("hello"),
'b': lambda: (
print("goodbye," + local),
print("!")),
}) |
我一直试图重新定义开关,这样我就可以摆脱"lambda:",但放弃了。调整定义:
1 2 3 4 5 6 7 8
| def switch(value, *maps):
options = {}
for m in maps:
options.update(m)
if value in options:
options[value]()
elif None in options:
options[None]() |
允许我将多个案例映射到同一代码,并提供默认选项:
1 2 3 4 5 6 7 8 9 10 11
| def sample(x):
switch(x, {
_: lambda: print("other")
for _ in 'cdef'
}, {
'a': lambda: print("hello"),
'b': lambda: (
print("goodbye,"),
print("!")),
None: lambda: print("I dunno")
}) |
每个复制的案例都必须在自己的字典中;switch()在查找值之前合并字典。它仍然比我想要的更丑,但是它具有在表达式上使用散列查找的基本效率,而不是通过所有键进行循环。
到目前为止,已经有很多答案说,"我们在Python中没有开关,请这样做"。然而,我想指出的是,switch语句本身是一个容易被滥用的构造,在大多数情况下可以并且应该避免这种构造,因为它们促进了懒惰的编程。例证:
1 2 3 4 5 6 7 8 9 10
| def ToUpper(lcChar):
if (lcChar == 'a' or lcChar == 'A'):
return 'A'
elif (lcChar == 'b' or lcChar == 'B'):
return 'B'
...
elif (lcChar == 'z' or lcChar == 'Z'):
return 'Z'
else:
return None # or something |
现在,您可以使用switch语句来完成这项工作(如果Python提供了一个switch语句),但是您将浪费时间,因为有一些方法可以很好地完成这项工作。或者,你有一些不太明显的东西:
1 2 3 4 5 6 7 8 9
| def ConvertToReason(code):
if (code == 200):
return 'Okay'
elif (code == 400):
return 'Bad Request'
elif (code == 404):
return 'Not Found'
else:
return None |
然而,这种操作可以并且应该用字典来处理,因为它将更快、更不复杂、更不容易出错和更紧凑。 绝大多数switch语句的"用例"都属于这两种情况中的一种;如果您已经彻底考虑了您的问题,那么使用它的理由就非常少。 所以,与其问"我如何在Python中切换?",也许我们应该问,"为什么我要在python中切换?"因为这通常是一个更有趣的问题,而且会暴露出你正在建造的任何建筑的设计缺陷。 现在,这并不是说开关也不应该被使用。状态机、lexer、解析器和automata在某种程度上都使用它们,一般来说,当您从对称输入开始并转到不对称输出时,它们是有用的;您只需要确保不将开关用作锤子,因为您在代码中看到了一堆钉子。
正如大多数人使用dict所展示的那样,将某个键映射到某个代码并不是真正的问题。真正的诀窍是尝试模拟整个直接插入和中断过程。我想我从来没有写过一个案例陈述,我用过这个"特性"。这里有一个直通车。
1 2 3 4 5 6
| def case(list): reduce(lambda b, f: (b | f[0], {False:(lambda:None),True:f[1]}[b | f[0]]())[0], list, False)
case([
(False, lambda:print(5)),
(True, lambda:print(4))
]) |
我真的把它想象成一句话。我希望你能原谅这种愚蠢的格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| reduce(
initializer=False,
function=(lambda b, f:
( b | f[0]
, { False: (lambda:None)
, True : f[1]
}[b | f[0]]()
)[0]
),
iterable=[
(False, lambda:print(5)),
(True, lambda:print(4))
]
) |
希望这是有效的python。它应该能让你中途下车。当然,布尔检查可以是表达式,如果您希望延迟地对它们进行计算,可以将它们全部包装在lambda中。在执行了列表中的一些项目之后,我也不难让它被接受。只需创建tuple(bool、bool、function),其中第二个bool指示是否要中断或中断。
作为Mark Biek答案的一个微小变化,对于这种不常见的情况,比如用户有一系列函数调用要延迟,需要打包参数(这不值得构建一系列不在线的函数),而不是这样:
1 2 3 4 5 6 7 8
| d = {
"a1": lambda: a(1),
"a2": lambda: a(2),
"b": lambda: b("foo"),
"c": lambda: c(),
"z": lambda: z("bar", 25),
}
return d[string]() |
…你可以这样做:
1 2 3 4 5 6 7 8 9
| d = {
"a1": (a, 1),
"a2": (a, 2),
"b": (b,"foo"),
"c": (c,)
"z": (z,"bar", 25),
}
func, *args = d[string]
return func(*args) |
这当然更短,但它是否更具可读性是一个悬而未决的问题… 我认为从lambda切换到partial对于这种特殊用途可能更易读(虽然不简单):
1 2 3 4 5 6 7 8
| d = {
"a1": partial(a, 1),
"a2": partial(a, 2),
"b": partial(b,"foo"),
"c": c,
"z": partial(z,"bar", 25),
}
return d[string]() |
…它的优点是可以很好地处理关键字参数:
1 2 3 4 5 6 7 8 9
| d = {
"a1": partial(a, 1),
"a2": partial(a, 2),
"b": partial(b,"foo"),
"c": c,
"k": partial(k, key=int),
"z": partial(z,"bar", 25),
}
return d[string]() |
虽然已经有足够的答案,但我想指出一个更简单、更强大的解决方案:
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
| class Switch:
def __init__(self, switches):
self.switches = switches
self.between = len(switches[0]) == 3
def __call__(self, x):
for line in self.switches:
if self.between:
if line[0] <= x < line[1]:
return line[2]
else:
if line[0] == x:
return line[1]
return None
if __name__ == '__main__':
between_table = [
(1, 4, 'between 1 and 4'),
(4, 8, 'between 4 and 8')
]
switch_between = Switch(between_table)
print('Switch Between:')
for i in range(0, 10):
if switch_between(i):
print('{} is {}'.format(i, switch_between(i)))
else:
print('No match for {}'.format(i))
equals_table = [
(1, 'One'),
(2, 'Two'),
(4, 'Four'),
(5, 'Five'),
(7, 'Seven'),
(8, 'Eight')
]
print('Switch Equals:')
switch_equals = Switch(equals_table)
for i in range(0, 10):
if switch_equals(i):
print('{} is {}'.format(i, switch_equals(i)))
else:
print('No match for {}'.format(i)) |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| Switch Between:
No match for 0
1 is between 1 and 4
2 is between 1 and 4
3 is between 1 and 4
4 is between 4 and 8
5 is between 4 and 8
6 is between 4 and 8
7 is between 4 and 8
No match for 8
No match for 9
Switch Equals:
No match for 0
1 is One
2 is Two
No match for 3
4 is Four
5 is Five
No match for 6
7 is Seven
8 is Eight
No match for 9 |
与abarnett的回答类似,这里有一个解决方案专门用于为交换机中的每个"case"调用单个函数的用例,同时避免lambda或partial的超简洁性,同时仍然能够处理关键字参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class switch(object):
NO_DEFAULT = object()
def __init__(self, value, default=NO_DEFAULT):
self._value = value
self._result = default
def __call__(self, option, func, *args, **kwargs):
if self._value == option:
self._result = func(*args, **kwargs)
return self
def pick(self):
if self._result is switch.NO_DEFAULT:
raise ValueError(self._value)
return self._result |
示例用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def add(a, b):
return a + b
def double(x):
return 2 * x
def foo(**kwargs):
return kwargs
result = (
switch(3)
(1, add, 7, 9)
(2, double, 5)
(3, foo, bar=0, spam=8)
(4, lambda: double(1 / 0)) # if evaluating arguments is not safe
).pick()
print(result) |
请注意,这是链接调用,即switch(3)(...)(...)(...)。不要在中间加逗号。把它们放在一个表达式中也很重要,这就是为什么我在隐式行继续的主调用周围使用了额外的括号。 如果您打开一个未处理的值,例如switch(5)(1, ...)(2, ...)(3, ...),上面的示例将引发一个错误。您可以提供默认值,例如switch(5, default=-1)...返回-1。
我从python文档中找到了以下最有用的答案: 你可以用一系列的if... elif... elif... else很容易地做到这一点。对于switch语句语法有一些建议,但是对于是否以及如何进行范围测试还没有达成共识。有关完整的详细信息和当前状态,请参阅PEP 275。 对于需要从大量可能性中进行选择的情况,可以创建一个字典,将事例值映射到要调用的函数。例如:
1 2 3 4 5 6 7 8 9
| def function_1(...):
...
functions = {'a': function_1,
'b': function_2,
'c': self.method_1, ...}
func = functions[value]
func() |
对于对对象调用方法,可以使用getAttr()内置的检索具有特定名称的方法进一步简化:
1 2 3 4 5 6 7 8
| def visit_a(self, ...):
...
...
def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method() |
建议您对方法名使用前缀,例如本例中的visit_。如果没有这样的前缀,如果值来自不受信任的源,攻击者将能够调用对象上的任何方法。
我已经做了一个不太在外部使用ifs的switch-case实现(它仍然在类中使用if)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class SwitchCase(object):
def __init__(self):
self._cases = dict()
def add_case(self,value, fn):
self._cases[value] = fn
def add_default_case(self,fn):
self._cases['default'] = fn
def switch_case(self,value):
if value in self._cases.keys():
return self._cases[value](value)
else:
return self._cases['default'](0) |
像这样使用:
1 2 3 4 5 6 7 8 9
| from switch_case import SwitchCase
switcher = SwitchCase()
switcher.add_case(1, lambda x:x+1)
switcher.add_case(2, lambda x:x+3)
switcher.add_default_case(lambda _:[1,2,3,4,5])
print switcher.switch_case(1) #2
print switcher.switch_case(2) #5
print switcher.switch_case(123) #[1, 2, 3, 4, 5] |
还可以使用列表存储案例,并通过选择调用相应的函数-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| cases = ['zero()','one()','two()','three()']
def zero():
print"method for 0 called..."
def one():
print"method for 1 called..."
def two():
print"method for 2 called..."
def three():
print"method for 3 called..."
i = int(raw_input("Enter choice between 0-3"))
if(i<=len(cases)):
exec(cases[i])
else:
print"wrong choice" |
在螺丝台上也解释过
格雷格的解决方案不适用于不可清洗的条目。例如,在索引lists时。
1 2 3 4 5
| # doesn't work
def give_me_array(key)
return {
[1, 0]:"hello"
}[key] |
幸运的是,虽然tuples是可以散列的。
1 2 3 4 5
| # works
def give_me_array(key)
return {
(1, 0):"hello"
}[tuple(key)] |
同样,字典或集合的版本也可能是不可变的(因此可能是哈希的)。
当我需要一个简单的开关案例来调用一系列方法,而不只是打印一些文本时,下面的内容适用于我的情况。在玩了lambda和globals之后,我觉得这是迄今为止最简单的选择。也许它也能帮助别人:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def start():
print("Start")
def stop():
print("Stop")
def print_help():
print("Help")
def choose_action(arg):
return {
"start": start,
"stop": stop,
"help": print_help,
}.get(arg, print_help)
argument = sys.argv[1].strip()
choose_action(argument)() # calling a method from the given string |
还有另一个选择:
1 2 3 4 5 6 7 8
| def fnc_MonthSwitch(int_Month): #### Define a function take in the month variable
str_Return ="Not Found" #### Set Default Value
if int_Month==1: str_Return ="Jan"
if int_Month==2: str_Return ="Feb"
if int_Month==3: str_Return ="Mar"
return str_Return; #### Return the month found
print ("Month Test 3: " + fnc_MonthSwitch( 3) )
print ("Month Test 14:" + fnc_MonthSwitch(14) ) |
我只使用if/elif/else语句。我认为它足以取代switch语句。
易于记忆:
1 2 3 4 5 6 7 8 9 10 11 12 13
| while True:
try:
x = int(input("Enter a numerical input:"))
except:
print("Invalid input - please enter a Integer!");
if x==1:
print("good");
elif x==2:
print("bad");
elif x==3:
break
else:
print ("terrible"); |
如果您实际上只是返回一个预先确定的固定值,那么您可以创建一个字典,其中所有可能的输入索引都是键,以及它们对应的值。另外,您可能并不真正希望函数这样做——除非您正在以某种方式计算返回值。 哦,如果你想做些像这样的转换,看这里。
switch语句只是if/elif/else的语法糖。任何控制语句所做的都是基于特定条件委托作业-决策路径。为了将其封装到模块中并能够基于其唯一ID调用作业,可以利用继承和python中的任何方法都是虚拟的这一事实来提供派生类特定的作业实现,如特定的"case"处理程序
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
| #!/usr/bin/python
import sys
class Case(object):
"""
Base class wich specifies the interface for"case" handler.
The all required arbitrary arguments inside"execute" method will be
provided through the derived class
specific constructor
@note in python, all class methods are virtual
"""
def __init__(self, id):
self.id = id
def pair(self):
"""
Pairs the given id of the"case" with
the instance on which"execute" will be called
"""
return (self.id, self)
def execute(self):#base class virtual method that needs to be overrided
pass
class Case1(Case):
def __init__(self, id, msg):
self.id = id
self.msg = msg
def execute(self):#override the base class method
print("<Case1> id={}, message: "{}"".format(str(self.id), self.msg))
class Case2(Case):
def __init__(self, id, n):
self.id = id
self.n = n
def execute(self):#override the base class method
print("<Case2> id={}, n={}.".format(str(self.id), str(self.n)))
print("
".join(map(str, range(self.n))))
class Switch(object):
"""
The class wich delegates the jobs
based on the given job id
"""
def __init__(self, cases):
self.cases = cases#dictionary: time complexitiy for access operation is 1
def resolve(self, id):
try:
cases[id].execute()
except KeyError as e:
print("Given id: {} is wrong!".format(str(id)))
if __name__ == '__main__':
# Cases
cases=dict([Case1(0,"switch").pair(), Case2(1, 5).pair()])
switch = Switch(cases)
# id will be dynamically specified
switch.resolve(0)
switch.resolve(1)
switch.resolve(2) |
switch语句只是语法上的糖分,这可能是Python没有它的原因。您可以轻松地使用if-else语句来实现此功能。 正如马修·辛克尔所说,你可以使用if和elif等。 像大多数switch语句一样具有"贯穿"功能也是一件简单的事情。你所要做的就是不要使用elif。
1 2 3 4 5 6
| if x == 1:
# 1
if x == 2:
# fall-through
elif x == 3:
# not fall-through |
1 2
| def f(x):
return {'a': 1,'b': 2,}.get(x) or"Default" |
|