Python中的switch-case无效;需要其他模式
我需要帮忙处理一些代码。我想在Python中实现switch case的模式,像一些教程说的那样,我可以用字典来实现,但我遇到了问题:
# Type can be either create or update or ..
message = { 'create':msg(some_data),
'update':msg(other_data)
# can have more
}
return message(type)
这个方法对我来说不管用,因为some_data或other_data可能是None(如果是None就会报错),而msg函数需要简单一点(我不想在里面加条件)。
问题在于,msg()函数每次填充字典的时候都会被执行。和其他编程语言中的switch case模式不同,后者通常只有在匹配时才会执行case
里的代码。
有没有其他方法可以做到这一点,还是说我只能用if和elif呢?
其实,情况更像这样:
message = { 'create': "blabla %s" % msg(some_data),
'update': "blabla %s" % msg(other_data)
'delete': "blabla %s" % diff(other_data, some_data)
}
所以lambda在这里不管用,而且调用的函数也不一样,所以我需要的更像一个真正的switch case,可能我得考虑其他的模式。
9 个回答
结果发现我被玩弄了,五年前在开关语句的游戏中我就已经被“击败”了,而那时我还没学会Python:没有使用lambda或字典的可读开关结构。唉,没办法。下面会介绍另一种做法。
这里有一个开关语句(经过@martineau的一些清理):
with switch(foo):
@case(1)
def _():
print "1"
@case(2)
def _():
print "2"
@case(3)
def _():
print "3"
@case(5)
@case(6)
def _():
print '5 and 6'
@case.default
def _():
print 'default'
我还会免费提供一个(中等程度)修改过的栈,滥用的装饰器和有点问题的上下文管理器。虽然看起来不太好,但它能工作(而且不是那种好用的)。基本上,它就是把字典的逻辑包裹在一个丑陋的外壳里。
import inspect
class switch(object):
def __init__(self, var):
self.cases = {}
self.var = var
def __enter__(self):
def case(value):
def decorator(f):
if value not in self.cases:
self.cases[value] = f
return f
return decorator
def default(f):
self.default = f
return f
case.default = default
f_locals = inspect.currentframe().f_back.f_locals
self.f_locals = f_locals.copy()
f_locals['case'] = case
def __exit__(self, *args, **kwargs):
new_locals = inspect.currentframe().f_back.f_locals
new_items = [key for key in new_locals if key not in self.f_locals]
for key in new_items:
del new_locals[key] # clean up
new_locals.update(self.f_locals) # this reverts all variables to their
try: # previous values
self.cases[self.var]()
except KeyError:
try:
getattr(self, 'default')()
except AttributeError:
pass
需要注意的是,这个修改过的栈其实并不是必须的。我们只是用它来创建一个作用域,这样在开关语句中的定义就不会泄漏到外面的作用域里,并且可以把case
引入到命名空间中。如果你不介意泄漏(比如循环会泄漏),那么你可以去掉这个栈的修改,直接从__enter__
返回case
装饰器,使用with语句的as
子句在外部作用域中接收它。
message = { 'create':msg(some_data or ''),
'update':msg(other_data or '')
# can have more
}
message = { 'create':(msg,some_data),
'update':(msg,other_data),
# can have more
}
func,data=message[msg_type]
func(data)
def msg(data):
if data is None: data=''
...
更好的是,为了防止 msg
被执行只是为了填充字典:
现在你可以自由地定义一个更合理的 msg
函数,这个函数可以处理一个等于 None
的参数:
听起来你把事情搞得比需要的复杂了。如果你想要简单点,就用:
if mytype == 'create':
return msg(some_data)
elif mytype == 'update':
return msg(other_data)
else:
return msg(default_data)
你并不一定要用字典和函数引用,只因为你可以。有时候,一个简单明了的if/else
结构正是你需要的。这样即使是你团队里最初学编程的人也能看懂,而且不会不必要地调用msg()。我还敢打赌,这种方法会比你之前在用的其他方案更快,除非情况很多,而msg()又特别快。