Python中的switch-case无效;需要其他模式

4 投票
9 回答
3099 浏览
提问于 2025-04-16 05:10

我需要帮忙处理一些代码。我想在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 个回答

4

结果发现我被玩弄了,五年前在开关语句的游戏中我就已经被“击败”了,而那时我还没学会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子句在外部作用域中接收它。

9
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 的参数:

9

听起来你把事情搞得比需要的复杂了。如果你想要简单点,就用:

if mytype == 'create':
    return msg(some_data)
elif mytype == 'update':
    return msg(other_data)
else:
    return msg(default_data)

你并不一定要用字典和函数引用,只因为你可以。有时候,一个简单明了的if/else结构正是你需要的。这样即使是你团队里最初学编程的人也能看懂,而且不会不必要地调用msg()。我还敢打赌,这种方法会比你之前在用的其他方案更快,除非情况很多,而msg()又特别快。

撰写回答