python中的switch case不起作用;需要另一种模式

时间:2010-10-07 23:22:11

标签: python design-patterns

我需要一个代码帮助,我想在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(如果没有则会引发错误)并且msg函数需要很简单(我不想在其中加入一些条件)。

这里的问题是每次执行函数msg()以填充dict 与切换案例模式不同,通常使用其他编程语言的人不会执行case中的代码,除非它是匹配的。

还有其他方法可以做到这一点,或者我只需要做,如果elif ......

更新   谢谢你所有的回复,但实际上它更像是这个

message = { 'create': "blabla %s" % msg(some_data),
            'update': "blabla %s" % msg(other_data)
            'delete': "blabla %s" % diff(other_data, some_data)
           }

所以lambda在这里不起作用而且没有调用相同的函数,所以它更像是我需要的真实开关案例,也许我必须考虑其他模式。

9 个答案:

答案 0 :(得分:9)

message = { 'create':msg(some_data or ''),
            'update':msg(other_data or '')
            # can have more
          }

更好的是,为了防止msg被执行只是为了填补字典:

message = { 'create':(msg,some_data),
            'update':(msg,other_data),
            # can have more
          }
func,data=message[msg_type]
func(data)

现在您可以自由定义一个更合理的msg函数,该函数可以处理等于None的参数:

def msg(data):
    if data is None: data=''
    ...

答案 1 :(得分:9)

这听起来比你需要的更复杂。你想要简单吗?

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

有时,一个无聊,明确的if/else块正是您所需要的。即使是团队中最新的程序员也很清楚,并且不会不必要地调用msg()。我也愿意打赌,这将比你正在处理的其他解决方案更快,除非案例数量增长并且msg()闪电般快。

答案 2 :(得分:3)

在我学习python之前5年,我在开关发明游戏中突然出现了笑话:Readable switch construction without lambdas or dictionaries。那好吧。请阅读下面的另一种方法。


下面。有一个switch语句。 (@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

请注意,实际上并不需要被黑客堆栈。我们只是使用它来创建一个范围,以便switch语句中出现的定义不会泄漏到封闭范围内并将case放入命名空间。如果你不介意泄漏,(例如循环泄漏),那么你可以使用with语句中的case子句删除堆栈hack并从__enter__返回as装饰器在封闭范围内接收它。

答案 3 :(得分:2)

您可以在lambda中隐藏评估:

message = { 'create': lambda: msg(some_data),
            'update': lambda: msg(other_data),
          }
return message[type]()

只要名称全部被定义(所以你没有得到NameError),你也可以这样构造它:

message = { 'create': (msg, some_data),
            'update': (other_func, other_data),
          }
func, arg = message[type]
return func(arg)

答案 4 :(得分:2)

这里有点不同(虽然有点类似于@ Tumbleweed),可以说更像是“面向对象”。您可以使用Python类(包含字典),而不是显式使用字典来处理各种情况。

这种方法为Python代码提供了一个相当自然的C / C ++ switch语句转换。与后者一样,它推迟处理每个案例的代码的执行,并允许提供默认的代码。

与案例对应的每个switch类方法的代码可以包含多行代码,而不是此处显示的单个return <expression>代码,并且只编译一次。但是,一个区别或限制是,一种方法中的处理将不会也不能自动“落入”下一代码的代码中(这不是示例中的问题,而是好的)。

class switch:
    def create(self): return "blabla %s" % msg(some_data)
    def update(self): return "blabla %s" % msg(other_data)
    def delete(self): return "blabla %s" % diff(other_data, some_data)
    def _default(self): return "unknown type_"
    def __call__(self, type_): return getattr(self, type_, self._default)()

switch = switch() # only needed once

return switch(type_)

答案 5 :(得分:1)

您可以使用lambda延迟执行匹配:

return {
    'create': lambda: msg(some_data),
    'update': lambda: msg(other_data),
    # ...
}[type]()

如果所有案例都只是使用不同的参数调用msg,您可以将其简化为:

return msg({
    'create': some_data,
    'update': other_data,
    # ...
}[type])

答案 6 :(得分:1)

如何创建新类并将数据/参数包装在对象中 - 所以不要通过传递参数来绑定数据 - 你可以让函数决定它需要哪些参数......

class MyObj(object):
        def __init__(self, data, other_data):
                self.data = data
                self.other_data = other_data

        def switch(self, method_type):
                return {
                                "create": self.msg,
                                "update": self.msg,
                                "delete": self.delete_func,
                        }[method_type]()

        def msg(self):
                #process self.data
                return "Hello, World  !!"

        def delete_func(self):
                #process other data self.other_data or anything else....
                return True

if "__main__" == __name__:
        m1 = MyObj(1,2)
        print m1.switch('create')
        print m1.switch('delete')

答案 7 :(得分:0)

啊没关系,这解释了。我在想elif http://bytebaker.com/2008/11/03/switch-case-statement-in-python/

答案 8 :(得分:0)

由于您希望在每种情况下执行的代码来自安全源,您可以将每个代码段存储在字典中的单独字符串表达式中,并按以下方式执行操作:

message = { 'create': '"blabla %s" % msg(some_data)',
            'update': '"blabla %s" % msg(other_data)',
            'delete': '"blabla %s" % diff(other_data, some_data)'
          }

return eval(message[type_])

最后一行的表达式也可以是eval(message.get(type_, '"unknown type_"'))以提供默认值。无论哪种方式,为了保持可读性,可以隐藏丑陋的细节:

switch = lambda type_: eval(message.get(type_, '"unknown type_"'))

return switch(type_)

代码片段甚至可以以更快的速度进行预编译:

from compiler import compile # deprecated since version 2.6: Removed in Python 3

for k in message:
    message[k] = compile(message[k], 'message case', 'eval')