我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
当前回答
我使用的解决方案:
这里发布的两个解决方案的组合,相对容易阅读,并支持默认值。
result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}.get(whatToUse, lambda x: x - 22)(value)
哪里
.get('c', lambda x: x - 22)(23)
在dict中查找“lambda x:x-2”,并在x=23时使用它
.get('xxx', lambda x: x - 22)(44)
在dict中找不到它,使用默认的“lambda x:x-22”,x=44。
其他回答
如果你有一个复杂的大小写块,你可以考虑使用函数字典查找表。。。
如果您以前没有这样做过,那么最好进入调试器并查看字典如何查找每个函数。
注意:不要在大小写/字典查找中使用“()”,否则将在创建字典/大小写块时调用每个函数。记住这一点,因为您只想使用哈希样式查找调用每个函数一次。
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()
Python 3.10(2021)引入了match-case语句,该语句提供了Pythons“switch”的一流实现。例如:
def f(x):
match x:
case 'a':
return 1
case 'b':
return 2
case _:
return 0 # 0 is the default case if x is not found
match-case语句比这个简单的示例强大得多。
以下原始答案写于2008年,当时还未提供匹配案例:
你可以用字典:
def f(x):
return {
'a': 1,
'b': 2,
}[x]
到目前为止,已经有很多回答说,“我们在Python中没有开关,这样做吧”。然而,我想指出,switch语句本身是一个容易被滥用的构造,在大多数情况下可以而且应该避免,因为它们促进了惰性编程。案例说明:
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语句)来执行此操作,但这会浪费您的时间,因为有些方法可以很好地执行此操作。或者,你有一些不太明显的东西:
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中进行切换?”因为这往往是一个更有趣的问题,而且往往会暴露出您正在构建的任何设计中的缺陷。
现在,这并不是说也不应该使用开关。状态机、词法分析器、解析器和自动机都在某种程度上使用它们,一般来说,当你从对称输入开始到非对称输出时,它们会很有用;您只需要确保不要将开关用作锤子,因为您在代码中看到了一堆钉子。
我发现一种常见的交换机结构:
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到单分派类型切换的混合。