我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
当前回答
作为Mark Biek答案的一个小变化,对于像这样的不常见情况,用户有一堆函数调用要延迟,而参数要打包(而且不值得构建一堆不符合逻辑的函数),而不是这样:
d = {
"a1": lambda: a(1),
"a2": lambda: a(2),
"b": lambda: b("foo"),
"c": lambda: c(),
"z": lambda: z("bar", 25),
}
return d[string]()
…您可以这样做:
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可能更容易理解(虽然不是更简单):
d = {
"a1": partial(a, 1),
"a2": partial(a, 2),
"b": partial(b, "foo"),
"c": c,
"z": partial(z, "bar", 25),
}
return d[string]()
…它的优点是可以很好地处理关键字参数:
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]()
其他回答
扩展“dict as switch”思想。如果要为交换机使用默认值:
def f(x):
try:
return {
'a': 1,
'b': 2,
}[x]
except KeyError:
return 'default'
如果您不担心丢失案例套件中的语法高亮显示,可以执行以下操作:
exec {
1: """
print ('one')
""",
2: """
print ('two')
""",
3: """
print ('three')
""",
}.get(value, """
print ('None')
""")
其中value是值。在C中,这将是:
switch (value) {
case 1:
printf("one");
break;
case 2:
printf("two");
break;
case 3:
printf("three");
break;
default:
printf("None");
break;
}
我们还可以创建一个helper函数来实现这一点:
def switch(value, cases, default):
exec cases.get(value, default)
因此,我们可以将其用于一、二和三的示例:
switch(value, {
1: """
print ('one')
""",
2: """
print ('two')
""",
3: """
print ('three')
""",
}, """
print ('None')
""")
我最喜欢的是一个非常好的食谱。这是我见过的最接近实际switchcase语句的语句,尤其是在特性中。
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
下面是一个示例:
# 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!"
一些评论指出,使用foo作为case而不是foo中的case的上下文管理器解决方案可能更干净,对于大型switch语句,线性而不是二次行为可能是一个不错的选择。使用for循环的答案的一部分价值是有中断和突破的能力,如果我们愿意稍微使用我们选择的关键字,我们也可以在上下文管理器中实现这一点:
class Switch:
def __init__(self, value):
self.value = value
self._entered = False
self._broken = False
self._prev = None
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
return False # Allows a traceback to occur
def __call__(self, *values):
if self._broken:
return False
if not self._entered:
if values and self.value not in values:
return False
self._entered, self._prev = True, values
return True
if self._prev is None:
self._prev = values
return True
if self._prev != values:
self._broken = True
return False
if self._prev == values:
self._prev = None
return False
@property
def default(self):
return self()
下面是一个示例:
# Prints 'bar' then 'baz'.
with Switch(2) as case:
while case(0):
print('foo')
while case(1, 2, 3):
print('bar')
while case(4, 5):
print('baz')
break
while case.default:
print('default')
break
还可以使用列表存储案例,并通过select调用相应的函数-
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"
也在螺丝台上进行了解释。
我从Twisted Python代码中学到了一种模式。
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,根据我们的情况进行了修改)如下
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”)。这被标记为['HELO','foo.bar.com']。实际的方法查找名称取自部件[0]。
(原始方法也称为state_COMMAND,因为它使用相同的模式来实现状态机,即getattr(self,'state_'+self.mode))