Replacements for switch statement in Python?
我想用python编写一个函数,它根据输入索引的值返回不同的固定值。
在其他语言中,我会使用
你可以用字典:
1 2 3 4 5 | def f(x): return { 'a': 1, 'b': 2, }[x] |
如果您希望使用默认值,可以使用字典
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' |
您可以随时使用它来分派一个令牌并执行扩展的代码段。在状态机中,您可以使用
编辑:具体使用方法
如果是smtp,您将收到来自电线的
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 |
你会收到
(原始方法也称为
我最喜欢的是一个非常好的食谱。你会很喜欢的。它是我见过的最接近实际的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) |
在口述中查找
1 | .get('xxx', lambda x: x - 22)(44) |
在dict中找不到它,并将默认的
这里的大多数答案都很老,尤其是那些被接受的答案,所以似乎值得更新。
首先,官方的python常见问题解答涵盖了这一点,并建议简单案例使用
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中的
有时也有Perl6风格匹配的建议,从
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 |
我喜欢马克·比斯的回答
由于
我得和
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' |
编辑:我注意到我可以在字典中使用
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. ') |
打印
我倾向于使用字典的解决方案是:
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 |
然后可以与
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安装:
1 | pip install NeoPySwitch |
如果您不担心在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') """) |
其中,
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) |
这当然更短,但它是否更具可读性是一个悬而未决的问题…
我认为从
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"调用单个函数的用例,同时避免
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) |
请注意,这是链接调用,即
如果您打开一个未处理的值,例如
我从python文档中找到了以下最有用的答案:
你可以用一系列的
对于需要从大量可能性中进行选择的情况,可以创建一个字典,将事例值映射到要调用的函数。例如:
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() |
建议您对方法名使用前缀,例如本例中的
我已经做了一个不太在外部使用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" |
在螺丝台上也解释过
格雷格的解决方案不适用于不可清洗的条目。例如,在索引
1 2 3 4 5 | # doesn't work def give_me_array(key) return { [1, 0]:"hello" }[key] |
幸运的是,虽然
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" |