我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
当前回答
我发现Python文档中的以下答案非常有用:
你可以通过一系列if…elif。。。埃利夫。。。其他的关于switch语句语法已经有了一些建议,但对于是否以及如何进行范围测试还没有达成共识。有关完整详细信息和当前状态,请参见PEP 275。
对于需要从大量可能性中进行选择的情况,可以创建一个字典,将大小写值映射到要调用的函数。例如:
def function_1(...):
...
functions = {'a': function_1,
'b': function_2,
'c': self.method_1, ...}
func = functions[value]
func()
对于在对象上调用方法,可以通过使用内置的getattr()来检索具有特定名称的方法来进一步简化:
def visit_a(self, ...):
...
...
def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method()
建议为方法名称使用前缀,例如本例中的visit_。如果没有这样的前缀,如果值来自不受信任的源,攻击者将能够调用对象上的任何方法。
其他回答
定义:
def switch1(value, options):
if value in options:
options[value]()
允许您使用相当简单的语法,并将案例绑定到映射中:
def sample1(x):
local = 'betty'
switch1(x, {
'a': lambda: print("hello"),
'b': lambda: (
print("goodbye," + local),
print("!")),
})
我一直试图用一种能让我摆脱“lambda:”的方式重新定义开关,但我放弃了。调整定义:
def switch(value, *maps):
options = {}
for m in maps:
options.update(m)
if value in options:
options[value]()
elif None in options:
options[None]()
允许我将多个案例映射到同一代码,并提供默认选项:
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()在查找值之前合并字典。它仍然比我想象的更丑,但它的基本效率是对表达式使用散列查找,而不是循环所有键。
我发现一种常见的交换机结构:
switch ...parameter...
case p1: v1; break;
case p2: v2; break;
default: v3;
可以用Python表示如下:
(lambda x: v1 if p1(x) else v2 if p2(x) else v3)
或以更清晰的方式格式化:
(lambda x:
v1 if p1(x) else
v2 if p2(x) else
v3)
Python版本不是一个语句,而是一个表达式,其计算结果为一个值。
这里的大多数答案都很陈旧,尤其是那些被接受的答案,因此似乎值得更新。
首先,官方的Python常见问题解答涵盖了这一点,并为简单案例推荐elif链,为更大或更复杂的案例推荐dict。它还建议在某些情况下使用一组visit_方法(许多服务器框架使用的样式):
def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method()
FAQ还提到了PEP275,它是为了让官方一劳永逸地决定添加C风格切换语句而编写的。但PEP实际上被推迟到了Python 3,它只是作为一个单独的提案PEP3103被正式拒绝。答案当然是否定的,但如果你对原因或历史感兴趣的话,这两位政治公众人物可以获得更多信息。
有一件事多次出现(在PEP 275中可以看到,尽管它是作为实际推荐删除的),那就是如果你真的为处理4种情况而烦恼的是8行代码,而不是C或Bash中的6行代码,你总是可以这样写:
if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place')
这并不是PEP 8所鼓励的,但它是可读的,并不是太单一。
自PEP 3103被拒绝以来的十多年里,C风格的案例陈述,甚至围棋中稍微更强大的版本,都被认为已经过时;每当有人提出python想法或-dev时,他们都会参考旧的决定。
然而,完全ML样式的模式匹配的想法每隔几年就会出现一次,特别是在Swift和Rust等语言采用它之后。问题是,如果没有代数数据类型,很难充分利用模式匹配。虽然圭多一直赞同这个想法,但没有人提出一个非常适合Python的方案。(你可以阅读我2014年的strawman作为一个例子。)这可能会随着3.7中的dataclass和一些零星的建议而改变,比如使用更强大的枚举来处理sum类型,或者使用不同类型的语句本地绑定的各种建议(如PEP3150,或者当前正在讨论的一组建议-ideas)。但到目前为止,它还没有。
偶尔也会有关于Perl 6样式匹配的建议,这基本上是从elif到regex到单分派类型切换的混合。
在阅读了公认的答案后,我感到非常困惑,但这一切都清楚了:
def numbers_to_strings(argument):
switcher = {
0: "zero",
1: "one",
2: "two",
}
return switcher.get(argument, "nothing")
该代码类似于:
function(argument){
switch(argument) {
case 0:
return "zero";
case 1:
return "one";
case 2:
return "two";
default:
return "nothing";
}
}
有关字典映射到函数的详细信息,请查看源代码。
我喜欢Mark Bies的回答
由于x变量必须使用两次,我将lambda函数修改为无参数。
我必须运行结果[value](value)
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类型。因此,这将模拟交换机;其他情况