我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
当前回答
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 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]
switch语句只是if/elif/else的语法糖。任何控制语句所做的都是基于某个条件(即决策路径)来授权作业。为了将其包装到模块中并能够基于其唯一id调用作业,可以使用继承和Python中的任何方法都是虚拟的这一事实来提供派生类特定的作业实现,作为特定的“case”处理程序:
#!/usr/bin/python
import sys
class Case(object):
"""
Base class which specifies the interface for the "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 overridden
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("\n".join(map(str, range(self.n))))
class Switch(object):
"""
The class which delegates the jobs
based on the given job id
"""
def __init__(self, cases):
self.cases = cases # dictionary: time complexity for the 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)
这里的大多数答案都很陈旧,尤其是那些被接受的答案,因此似乎值得更新。
首先,官方的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到单分派类型切换的混合。
我要把我的两分钱放在这里。Python中没有case/switch语句的原因是因为Python遵循“只有一种正确的方法”的原则。很明显,您可以想出各种方法来重新创建switch/case功能,但实现这一点的Python方法是if/elf构造。即。,
if something:
return "first thing"
elif somethingelse:
return "second thing"
elif yetanotherthing:
return "third thing"
else:
return "default thing"
我只是觉得PEP 8应该在这里获得认可。Python的一个优点是它的简单和优雅。这在很大程度上源于PEP8中提出的原则,包括“只有一种正确的方法可以做某事。”
我发现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_。如果没有这样的前缀,如果值来自不受信任的源,攻击者将能够调用对象上的任何方法。