python中的switch case不起作用;

switch case in python doesn't work; need another pattern

我需要一个关于代码的帮助,我想在python中实现switch case模式,就像一些教程所说的那样,我可以使用字典来实现这个功能,但我的问题是:

1
2
3
4
5
6
7
  # type can be either create or update or ..
  message = { 'create':msg(some_data),
              'update':msg(other_data)
              # can have more
            }

  return message(type)

但是它对我不起作用,因为有些数据或其他数据可能是无的(如果不是,它会引发错误),并且msg函数需要简单(我不想在其中加入某些条件)。

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

有没有其他方法可以这样做,或者我只需要这样做,如果伊利夫…

更新:谢谢你所有的回复,但实际上更像这样

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

所以lambda在这里不起作用,也不调用相同的函数,所以它更像是我需要的一个真正的开关案例,也许我需要考虑其他模式。


听起来你把这件事复杂化了。你想要简单点?

1
2
3
4
5
6
if mytype == 'create':
    return msg(some_data)
elif mytype == 'update':
    return msg(other_data)
else:
    return msg(default_data)

您不必仅仅因为可以使用dict和函数引用。有时,一个无聊的、明确的if/else块正是您需要的。很明显,即使是团队中最新的程序员也不会不必要地调用msg()。我也愿意打赌,这将比您正在研究的其他解决方案更快,除非案例数量增加并且msg()快得惊人。


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

更妙的是,为了防止执行msg只是为了完成这一指令:

1
2
3
4
5
6
message = { 'create':(msg,some_data),
            'update':(msg,other_data),
            # can have more
          }
func,data=message[msg_type]
func(data)

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

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


在我学习python之前的5年里,我被灌输了开关发明游戏:没有lambda或字典的可读开关结构。哦,好吧。阅读下面的另一种方法。

在这里。有一个switch语句。(有@martineau的一些不错的清洁服务)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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'

我将免费加入(适度的)黑客堆栈、滥用的装饰器和可疑的上下文管理器。它很难看,但功能正常(而且不是很好)。基本上,它所做的就是用一个丑陋的包装器包装字典逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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泄漏到命名空间中。如果您不介意泄漏(例如循环泄漏),那么您可以从__enter__中移除堆栈黑客并返回case修饰器,使用WITH语句上的as子句在封闭范围内接收它。


这里有一点不同(虽然有点类似于@torbeweed),而且可以说更"面向对象"。您可以使用Python类(包含字典)来代替显式使用字典来处理各种情况。

这种方法提供了一个看似自然的C/C++ EDCOX1,12个语句到Python代码的翻译。与后者一样,它推迟了处理每种情况的代码的执行,并允许提供默认的代码。

与案例对应的每个switch类方法的代码可以由多行代码组成,而不是此处显示的单个return 行代码,并且都只编译一次。但是,有一个区别或限制是,一个方法中的处理不会也不能自动"尽管"进入下一个方法的代码中(这在示例中不是问题,但很好)。

1
2
3
4
5
6
7
8
9
10
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_)

可以将计算隐藏在lambda中:

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

只要所有的名称都定义好了(这样你就不会得到一个NameError,你也可以这样构造它:

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


创建新的类并将数据/参数包装在对象中,这样就可以让函数决定它需要哪些参数,而不是通过传递参数来绑定数据…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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')


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

1
2
3
4
5
return {
    'create': lambda: msg(some_data),
    'update': lambda: msg(other_data),
    # ...
}[type]()

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

1
2
3
4
5
return msg({
    'create': some_data,
    'update': other_data,
    # ...
}[type])

由于在每种情况下要执行的代码都来自安全的源代码,因此可以将每个代码段存储在字典中的单独字符串表达式中,并沿着这些行执行一些操作:

1
2
3
4
5
6
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_"')),以提供默认值。无论哪种方式,为了保持事物的可读性,丑陋的细节都可以隐藏起来:

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

return switch(type_)

甚至可以更快地预编译代码片段:

1
2
3
4
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')

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