替换python中的switch语句?

替换python中的switch语句?

Replacements for switch statement in Python?

我想用python编写一个函数,它根据输入索引的值返回不同的固定值。

在其他语言中,我会使用switchcase语句,但python似乎没有switch语句。在这个场景中,推荐的python解决方案是什么?


你可以用字典:

1
2
3
4
5
def f(x):
    return {
        'a': 1,
        'b': 2,
    }[x]

如果您希望使用默认值,可以使用字典get(key[, default])方法:

1
2
3
4
5
def f(x):
    return {
        'a': 1,
        'b': 2
    }.get(x, 9)    # 9 is default if x not found

我一直喜欢这样做

1
2
3
4
5
result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}[value](x)

从这里


除了字典方法(我真的很喜欢,btw),您还可以使用if elif else获得开关/案例/默认功能:

1
2
3
4
5
6
7
8
9
10
if x == 'a':
    # Do the thing
elif x == 'b':
    # Do the other thing
if x in 'bc':
    # Fall-through by not using elif, but now the default case includes case 'a'!
elif x in 'xyz':
    # Do yet another thing
else:
    # Do the default

当然,这和switch/case不一样——您不可能像不使用break;语句那样容易失败,但您可以进行更复杂的测试。它的格式比一系列嵌套的ifs要好,即使在功能上这是它更接近的。


我最喜欢的python开关/外壳配方是:

1
2
choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default')

简单而简短的场景。

与11+行C代码比较:

1
2
3
4
5
6
7
8
9
10
11
12
// C Language version of a simple 'switch/case'.
switch( key )
{
    case 'a' :
        result = 1;
        break;
    case 'b' :
        result = 2;
        break;
    default :
        result = -1;
}

甚至可以通过使用元组来分配多个变量:

1
2
choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))

1
2
3
4
5
6
7
8
class switch(object):
    value = None
    def __new__(class_, value):
        class_.value = value
        return True

def case(*args):
    return any((arg == switch.value for arg in args))

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while switch(n):
    if case(0):
        print"You typed zero."
        break
    if case(1, 4, 9):
        print"n is a perfect square."
        break
    if case(2):
        print"n is an even number."
    if case(2, 3, 5, 7):
        print"n is a prime number."
        break
    if case(6, 8):
        print"n is an even number."
        break
    print"Only single-digit numbers are allowed."
    break

测验:

1
2
3
4
5
6
7
n = 2
#Result:
#n is an even number.
#n is a prime number.
n = 11
#Result:
#Only single-digit numbers are allowed.

我从扭曲的Python代码中学习了一种模式。

1
2
3
4
5
6
7
8
9
10
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,针对我们的案例进行了修改)如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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')。这被标记为parts作为['HELO', 'foo.bar.com']。实际方法查找名称取自parts[0]

(原始方法也称为state_COMMAND,因为它使用相同的模式来实现状态机,即getattr(self, 'state_' + self.mode))


我最喜欢的是一个非常好的食谱。你会很喜欢的。它是我见过的最接近实际的switch case语句的语句,特别是在特性中。

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

下面是一个例子:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 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!"

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
class Switch:
    def __init__(self, value):
        self.value = value

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        return False # Allows a traceback to occur

    def __call__(self, *values):
        return self.value in values


from datetime import datetime

with Switch(datetime.today().weekday()) as case:
    if case(0):
        # Basic usage of switch
        print("I hate mondays so much.")
        # Note there is no break needed here
    elif case(1,2):
        # This switch also supports multiple conditions (in one line)
        print("When is the weekend going to be here?")
    elif case(3,4):
        print("The weekend is near.")
    else:
        # Default would occur here
        print("Let's go have fun!") # Didn't use case for example purposes

假设您不希望只返回一个值,而是希望使用更改对象上某些内容的方法。使用这里所述的方法将是:

1
2
3
4
result = {
  'a': obj.increment(x),
  'b': obj.decrement(x)
}.get(value, obj.default(x))

这里所发生的是,python计算字典中的所有方法。因此,即使您的值是"a",对象也将递增和递减x。

解决方案:

1
2
3
4
5
6
func, args = {
  'a' : (obj.increment, (x,)),
  'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))

result = func(*args)

所以你得到一个包含函数及其参数的列表。这样,只返回函数指针和参数列表,而不计算。'然后,result'计算返回的函数调用。


我只想把我的两分钱放在这里。python中没有case/switch语句的原因是因为python遵循"只有一种正确的方法可以做某事"的原则。很明显,您可以想出各种方法来重新创建开关/案例功能,但实现这一点的方法是if/elif构造。工业工程

1
2
3
4
5
6
7
8
if something:
    return"first thing"
elif somethingelse:
    return"second thing"
elif yetanotherthing:
    return"third thing"
else:
    return"default thing"

我只是觉得8号种子应该在这里点头。关于python的一个美丽之处就是它的简单和优雅。这在很大程度上源于我们在PEP 8中提出的原则,包括"只有一种正确的方法可以做某事"。


扩展"dict as switch"的概念。如果要对交换机使用默认值:

1
2
3
4
5
6
7
8
def f(x):
    try:
        return {
            'a': 1,
            'b': 2,
        }[x]
    except KeyError:
        return 'default'

如果您有一个复杂的事例块,可以考虑使用函数字典查找表…

如果您以前没有这样做,那么最好进入调试器并查看字典如何查找每个函数。

注意:不要在事例/字典查找中使用"()",否则在创建词典/事例块时,它将调用每个函数。记住这一点,因为您只希望使用哈希样式查找调用每个函数一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def first_case():
    print"first"

def second_case():
    print"second"

def third_case():
    print"third"

mycase = {
'first': first_case, #do not use ()
'second': second_case, #do not use ()
'third': third_case #do not use ()
}
myfunc = mycase['first']
myfunc()

如果您要搜索额外的语句,作为"switch",我构建了一个扩展python的python模块。它被称为ESpy,是"针对python的增强结构",它同时适用于python 2.x和python 3.x。

例如,在这种情况下,可以使用以下代码执行switch语句:

1
2
3
4
5
6
7
8
9
10
11
macro switch(arg1):
    while True:
        cont=False
        val=%arg1%
        socket case(arg2):
            if val==%arg2% or cont:
                cont=True
                socket
        socket else:
            socket
        break

可以这样使用:

1
2
3
4
5
6
7
8
9
a=3
switch(a):
    case(0):
        print("Zero")
    case(1):
        print("Smaller than 2"):
        break
    else:
        print ("greater than 1")

所以espy用python将其翻译为:

1
2
3
4
5
6
7
8
9
10
11
12
a=3
while True:
    cont=False
    if a==0 or cont:
        cont=True
        print ("Zero")
    if a==1 or cont:
        cont=True
        print ("Smaller than 2")
        break
    print ("greater than 1")
    break


我在谷歌搜索中找不到简单的答案。但我还是想出来了。这真的很简单。决定张贴它,也许可以防止其他人的头上少一些划痕。关键是简单的"in"和元组。下面是switch语句与fall through的行为,包括随机fall through。

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
l = ['Dog', 'Cat', 'Bird', 'Bigfoot',
     'Dragonfly', 'Snake', 'Bat', 'Loch Ness Monster']

for x in l:
    if x in ('Dog', 'Cat'):
        x +=" has four legs"
    elif x in ('Bat', 'Bird', 'Dragonfly'):
        x +=" has wings."
    elif x in ('Snake',):
        x +=" has a forked tongue."
    else:
        x +=" is a big mystery by default."
    print(x)

print()

for x in range(10):
    if x in (0, 1):
        x ="Values 0 and 1 caught here."
    elif x in (2,):
        x ="Value 2 caught here."
    elif x in (3, 7, 8):
        x ="Values 3, 7, 8 caught here."
    elif x in (4, 6):
        x ="Values 4 and 6 caught here"
    else:
        x ="Values 5 and 9 caught in default."
    print(x)

提供:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Dog has four legs
Cat has four legs
Bird has wings.
Bigfoot is a big mystery by default.
Dragonfly has wings.
Snake has a forked tongue.
Bat has wings.
Loch Ness Monster is a big mystery by default.

Values 0 and 1 caught here.
Values 0 and 1 caught here.
Value 2 caught here.
Values 3, 7, 8 caught here.
Values 4 and 6 caught here
Values 5 and 9 caught in default.
Values 4 and 6 caught here
Values 3, 7, 8 caught here.
Values 3, 7, 8 caught here.
Values 5 and 9 caught in default.

我发现一种常见的开关结构:

1
2
3
4
switch ...parameter...
case p1: v1; break;
case p2: v2; break;
default: v3;

可以用python表示如下:

1
(lambda x: v1 if p1(x) else v2 if p2(x) else v3)

或以更清晰的方式格式化:

1
2
3
4
(lambda x:
     v1 if p1(x) else
     v2 if p2(x) else
     v3)

python版本不是一个语句,而是一个表达式,它的计算结果是一个值。


我使用的解决方案:

这里发布的两个解决方案的组合,相对容易读取,并且支持默认值。

1
2
3
4
5
result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}.get(whatToUse, lambda x: x - 22)(value)

哪里

1
.get('c', lambda x: x - 22)(23)

在口述中查找"lambda x: x - 2",与x=23一起使用。

1
.get('xxx', lambda x: x - 22)(44)

在dict中找不到它,并将默认的"lambda x: x - 22"x=44一起使用。


这里的大多数答案都很老,尤其是那些被接受的答案,所以似乎值得更新。

首先,官方的python常见问题解答涵盖了这一点,并建议简单案例使用elif链,较大或更复杂的案例使用dict。它还建议在某些情况下使用一组visit_方法(许多服务器框架使用的样式):

1
2
3
4
def dispatch(self, value):
    method_name = 'visit_' + str(value)
    method = getattr(self, method_name)
    method()

FAQ还提到了PEP 275,它是为了让一位官员一次性决定添加C样式的切换语句。但这个PEP实际上被推迟到了Python3,而它只是作为一个单独的提议被正式拒绝了,即PEP 3103。当然,答案是否定的,但是如果你对原因或历史感兴趣的话,这两个政治公众人物会链接到其他信息。

多次出现的一件事(可以在PEP 275中看到,即使它被删去作为一个实际的建议)是,如果你真的为8行代码来处理4个案例而烦恼,而不是你在C或bash中所用的6行代码,你总是可以这样写:

1
2
3
4
if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place')

PEP8并不鼓励这样做,但它可读性强,不太单一。

在PEP 3103被拒绝的十多年里,C风格的case-statements问题,甚至是Go中功能稍强的版本,都被认为是死路一条;每当有人提出关于python的想法或-dev,他们都会提到旧的决定。

然而,完整的ML样式模式匹配的思想每隔几年就出现一次,特别是像Swift和Rust这样的语言已经采用了它。问题是,没有代数数据类型的模式匹配很难得到更多的使用。尽管guido对此表示同情,但没有人提出一个适合python的建议。(例如,你可以阅读我的2014年战略报告。)这可能会随着3.7中的dataclass和一些零星的建议而改变,这些建议要求更强大的enum来处理总和类型,或者针对不同类型的声明和地方约束提出各种建议(如pep 3150,或者目前正在讨论的一组建议-想法)。但到目前为止,还没有。

有时也有Perl6风格匹配的建议,从elif到regex到单调度类型切换,基本上都是混乱的。


1
2
3
4
def f(x):
    dictionary = {'a':1, 'b':2, 'c':3}
    return dictionary.get(x,'Not Found')
##Returns the value for the letter x;returns 'Not Found' if x isn't a key in the dictionary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# simple case alternative

some_value = 5.0

# this while loop block simulates a case block

# case
while True:

    # case 1
    if some_value > 5:
        print ('Greater than five')
        break

    # case 2
    if some_value == 5:
        print ('Equal to five')
        break

    # else case 3
    print ( 'Must be less than 5')
    break

我喜欢马克·比斯的回答

由于x变量必须使用两次,所以我将lambda函数修改为无参数。

我得和results[value](value)一起跑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [2]: result = {
    ...:   'a': lambda x: 'A',
    ...:   'b': lambda x: 'B',
    ...:   'c': lambda x: 'C'
    ...: }
    ...: result['a']('a')
    ...:
Out[2]: 'A'

In [3]: result = {
    ...:   'a': lambda : 'A',
    ...:   'b': lambda : 'B',
    ...:   'c': lambda : 'C',
    ...:   None: lambda : 'Nothing else matters'

    ...: }
    ...: result['a']()
    ...:
Out[3]: 'A'

编辑:我注意到我可以在字典中使用None类型。所以这将模仿switch ; case else


1
2
3
4
def f(x):
     return 1 if x == 'a' else\
            2 if x in 'bcd' else\
            0 #default

简短易读,有一个默认值,在条件和返回值中都支持表达式。

但是,它比使用字典的解决方案效率低。例如,在返回默认值之前,python必须扫描所有条件。


简单,不测试;每个条件都是独立计算的:没有失败,但是所有情况都是计算的(尽管要打开的表达式只计算一次),除非有break语句。例如,

1
2
3
4
5
6
7
8
9
10
11
12
for case in [expression]:
    if case == 1:
        print(end='Was 1. ')

    if case == 2:
        print(end='Was 2. ')
        break

    if case in (1, 2):
        print(end='Was 1 or 2. ')

    print(end='Was something. ')

打印Was 1. Was 1 or 2. Was something.(该死!为什么在内联代码块中不能有尾随空格?)如果expression1进行评估,如果expression2进行评估,则Was 2.2进行评估,如果expression对其他东西进行评估,则Was something.expression进行评估。


我倾向于使用字典的解决方案是:

1
2
3
4
5
6
7
8
9
10
11
12
def decision_time( key, *args, **kwargs):
    def action1()
       """This function is a closure - and has access to all the arguments"""
        pass
    def action2()
       """This function is a closure - and has access to all the arguments"""
        pass
    def action3()
       """This function is a closure - and has access to all the arguments"""
        pass

   return {1:action1, 2:action2, 3:action3}.get(key,default)()

这样做的好处是,它不会每次都尝试评估函数,您只需确保外部函数获取内部函数所需的所有信息。


运行函数的解决方案:

1
2
3
4
5
6
result = {
    'case1':     foo1,
    'case2':     foo2,
    'case3':     foo3,
    'default':   default,
}.get(option)()

其中foo1()、foo2()、foo3()和default()是函数


我认为最好的方法是使用Python语言习语来保持代码的可测试性。如前面的答案所示,我使用字典来利用Python结构和语言,并以不同的方法隔离"case"代码。下面有一个类,但是您可以直接使用模块、全局和函数。类具有可以用隔离测试的方法。根据您的需要,您也可以使用静态方法和属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ChoiceManager:

    def __init__(self):
        self.__choice_table = \
        {
           "CHOICE1" : self.my_func1,
           "CHOICE2" : self.my_func2,
        }

    def my_func1(self, data):
        pass

    def my_func2(self, data):
        pass

    def process(self, case, data):
        return self.__choice_table[case](data)

ChoiceManager().process("CHOICE1", my_data)

利用这个方法,还可以使用类作为"uu choice_table"的键。这样,您就可以避免ISInstance滥用,并保持所有的清洁和可测试性。

假设您必须处理来自网络或MQ的大量消息或数据包。每个包都有自己的结构和管理代码(以通用方式)。使用上述代码,可以执行如下操作:

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
class PacketManager:

    def __init__(self):
        self.__choice_table = \
        {
            ControlMessage : self.my_func1,
            DiagnosticMessage : self.my_func2,
        }

    def my_func1(self, data):
        # process the control message here
        pass

    def my_func2(self, data):
        # process the diagnostic message here
        pass

    def process(self, pkt):
        return self.__choice_table[pkt.__class__](pkt)

pkt = GetMyPacketFromNet()
PacketManager().process(pkt)


# isolated test or isolated usage example
def test_control_packet():
    p = ControlMessage()
    PacketManager().my_func1(p)

因此,复杂性不是在代码流中扩散的,而是在代码结构中呈现的。


您可以使用发送的dict:

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
#!/usr/bin/env python


def case1():
    print("This is case 1")

def case2():
    print("This is case 2")

def case3():
    print("This is case 3")


token_dict = {
   "case1" : case1,
   "case2" : case2,
   "case3" : case3,
}


def main():
    cases = ("case1","case3","case2","case1")
    for case in cases:
        token_dict[case]()


if __name__ == '__main__':
    main()

输出:

1
2
3
4
This is case 1
This is case 3
This is case 2
This is case 1

读完答案后我很困惑,但这一切都清楚了:

1
2
3
4
5
6
7
def numbers_to_strings(argument):
    switcher = {
        0:"zero",
        1:"one",
        2:"two",
    }
    return switcher.get(argument,"nothing")

此代码类似于:

1
2
3
4
5
6
7
8
9
10
11
12
function(argument){
    switch(argument) {
        case 0:
            return"zero";
        case 1:
            return"one";
        case 2:
            return"two";
        default:
            return"nothing";
    }
}

有关字典映射到函数的更多信息,请查看源代码。


扩展Greg Hewgill的答案-我们可以使用装饰器封装字典解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def case(callable):
   """switch-case decorator"""
    class case_class(object):
        def __init__(self, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs

        def do_call(self):
            return callable(*self.args, **self.kwargs)

return case_class

def switch(key, cases, default=None):
   """switch-statement"""
    ret = None
    try:
        ret = case[key].do_call()
    except KeyError:
        if default:
            ret = default.do_call()
    finally:
        return ret

然后可以与@case装饰器一起使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@case
def case_1(arg1):
    print 'case_1: ', arg1

@case
def case_2(arg1, arg2):
    print 'case_2'
    return arg1, arg2

@case
def default_case(arg1, arg2, arg3):
    print 'default_case: ', arg1, arg2, arg3

ret = switch(somearg, {
    1: case_1('somestring'),
    2: case_2(13, 42)
}, default_case(123, 'astring', 3.14))

print ret

好消息是这已经在Neopyswitch模块中完成了。只需使用PIP安装:

1
pip install NeoPySwitch

如果您不担心在case套件中丢失语法突出显示,可以执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
exec {
    1:"""
print ('one')
"""
,
    2:"""
print ('two')
"""
,
    3:"""
print ('three')
"""
,
}.get(value,"""
print ('None')
"""
)

其中,value为数值。在c中,这是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
switch (value) {
    case 1:
        printf("one");
        break;
    case 2:
        printf("two");
        break;
    case 3:
        printf("three");
        break;
    default:
        printf("None");
        break;
}

我们还可以创建一个助手函数来执行此操作:

1
2
def switch(value, cases, default):
    exec cases.get(value, default)

因此,我们可以像这样将其用于示例1、2和3:

1
2
3
4
5
6
7
8
9
10
11
12
13
switch(value, {
    1:"""
print ('one')
   """
,
    2:"""
print ('two')
   """
,
    3:"""
print ('three')
   """
,
},"""
print ('None')
"""
)

定义:

1
2
3
def switch1(value, options):
  if value in options:
    options[value]()

允许您使用相当简单的语法,将案例捆绑到地图中:

1
2
3
4
5
6
7
8
def sample1(x):
  local = 'betty'
  switch1(x, {
    'a': lambda: print("hello"),
    'b': lambda: (
      print("goodbye," + local),
      print("!")),
    })

我一直试图重新定义开关,这样我就可以摆脱"lambda:",但放弃了。调整定义:

1
2
3
4
5
6
7
8
def switch(value, *maps):
  options = {}
  for m in maps:
    options.update(m)
  if value in options:
    options[value]()
  elif None in options:
    options[None]()

允许我将多个案例映射到同一代码,并提供默认选项:

1
2
3
4
5
6
7
8
9
10
11
def sample(x):
  switch(x, {
    _: lambda: print("other")
    for _ in 'cdef'
    }, {
    'a': lambda: print("hello"),
    'b': lambda: (
      print("goodbye,"),
      print("!")),
    None: lambda: print("I dunno")
    })

每个复制的案例都必须在自己的字典中;switch()在查找值之前合并字典。它仍然比我想要的更丑,但是它具有在表达式上使用散列查找的基本效率,而不是通过所有键进行循环。


到目前为止,已经有很多答案说,"我们在Python中没有开关,请这样做"。然而,我想指出的是,switch语句本身是一个容易被滥用的构造,在大多数情况下可以并且应该避免这种构造,因为它们促进了懒惰的编程。例证:

1
2
3
4
5
6
7
8
9
10
def ToUpper(lcChar):
    if (lcChar == 'a' or lcChar == 'A'):
        return 'A'
    elif (lcChar == 'b' or lcChar == 'B'):
        return 'B'
    ...
    elif (lcChar == 'z' or lcChar == 'Z'):
        return 'Z'
    else:
        return None        # or something

现在,您可以使用switch语句来完成这项工作(如果Python提供了一个switch语句),但是您将浪费时间,因为有一些方法可以很好地完成这项工作。或者,你有一些不太明显的东西:

1
2
3
4
5
6
7
8
9
def ConvertToReason(code):
    if (code == 200):
        return 'Okay'
    elif (code == 400):
        return 'Bad Request'
    elif (code == 404):
        return 'Not Found'
    else:
        return None

然而,这种操作可以并且应该用字典来处理,因为它将更快、更不复杂、更不容易出错和更紧凑。

绝大多数switch语句的"用例"都属于这两种情况中的一种;如果您已经彻底考虑了您的问题,那么使用它的理由就非常少。

所以,与其问"我如何在Python中切换?",也许我们应该问,"为什么我要在python中切换?"因为这通常是一个更有趣的问题,而且会暴露出你正在建造的任何建筑的设计缺陷。

现在,这并不是说开关也不应该被使用。状态机、lexer、解析器和automata在某种程度上都使用它们,一般来说,当您从对称输入开始并转到不对称输出时,它们是有用的;您只需要确保不将开关用作锤子,因为您在代码中看到了一堆钉子。


正如大多数人使用dict所展示的那样,将某个键映射到某个代码并不是真正的问题。真正的诀窍是尝试模拟整个直接插入和中断过程。我想我从来没有写过一个案例陈述,我用过这个"特性"。这里有一个直通车。

1
2
3
4
5
6
def case(list): reduce(lambda b, f: (b | f[0], {False:(lambda:None),True:f[1]}[b | f[0]]())[0], list, False)

case([
    (False, lambda:print(5)),
    (True, lambda:print(4))
])

我真的把它想象成一句话。我希望你能原谅这种愚蠢的格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
reduce(
    initializer=False,
    function=(lambda b, f:
        ( b | f[0]
        , { False: (lambda:None)
          , True : f[1]
          }[b | f[0]]()
        )[0]
    ),
    iterable=[
        (False, lambda:print(5)),
        (True, lambda:print(4))
    ]
)

希望这是有效的python。它应该能让你中途下车。当然,布尔检查可以是表达式,如果您希望延迟地对它们进行计算,可以将它们全部包装在lambda中。在执行了列表中的一些项目之后,我也不难让它被接受。只需创建tuple(bool、bool、function),其中第二个bool指示是否要中断或中断。


作为Mark Biek答案的一个微小变化,对于这种不常见的情况,比如用户有一系列函数调用要延迟,需要打包参数(这不值得构建一系列不在线的函数),而不是这样:

1
2
3
4
5
6
7
8
d = {
   "a1": lambda: a(1),
   "a2": lambda: a(2),
   "b": lambda: b("foo"),
   "c": lambda: c(),
   "z": lambda: z("bar", 25),
    }
return d[string]()

…你可以这样做:

1
2
3
4
5
6
7
8
9
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对于这种特殊用途可能更易读(虽然不简单):

1
2
3
4
5
6
7
8
d = {
   "a1": partial(a, 1),
   "a2": partial(a, 2),
   "b": partial(b,"foo"),
   "c": c,
   "z": partial(z,"bar", 25),
    }
return d[string]()

…它的优点是可以很好地处理关键字参数:

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


虽然已经有足够的答案,但我想指出一个更简单、更强大的解决方案:

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
39
40
41
42
43
44
45
46
47
class Switch:
    def __init__(self, switches):
        self.switches = switches
        self.between = len(switches[0]) == 3

    def __call__(self, x):
        for line in self.switches:
            if self.between:
                if line[0] <= x < line[1]:
                    return line[2]
            else:
                if line[0] == x:
                    return line[1]
        return None


if __name__ == '__main__':
    between_table = [
        (1, 4, 'between 1 and 4'),
        (4, 8, 'between 4 and 8')
    ]

    switch_between = Switch(between_table)

    print('Switch Between:')
    for i in range(0, 10):
        if switch_between(i):
            print('{} is {}'.format(i, switch_between(i)))
        else:
            print('No match for {}'.format(i))


    equals_table = [
        (1, 'One'),
        (2, 'Two'),
        (4, 'Four'),
        (5, 'Five'),
        (7, 'Seven'),
        (8, 'Eight')
    ]
    print('Switch Equals:')
    switch_equals = Switch(equals_table)
    for i in range(0, 10):
        if switch_equals(i):
            print('{} is {}'.format(i, switch_equals(i)))
        else:
            print('No match for {}'.format(i))

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Switch Between:
No match for 0
1 is between 1 and 4
2 is between 1 and 4
3 is between 1 and 4
4 is between 4 and 8
5 is between 4 and 8
6 is between 4 and 8
7 is between 4 and 8
No match for 8
No match for 9

Switch Equals:
No match for 0
1 is One
2 is Two
No match for 3
4 is Four
5 is Five
No match for 6
7 is Seven
8 is Eight
No match for 9

与abarnett的回答类似,这里有一个解决方案专门用于为交换机中的每个"case"调用单个函数的用例,同时避免lambdapartial的超简洁性,同时仍然能够处理关键字参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class switch(object):
    NO_DEFAULT = object()

    def __init__(self, value, default=NO_DEFAULT):
        self._value = value
        self._result = default

    def __call__(self, option, func, *args, **kwargs):
        if self._value == option:
            self._result = func(*args, **kwargs)
        return self

    def pick(self):
        if self._result is switch.NO_DEFAULT:
            raise ValueError(self._value)

        return self._result

示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def add(a, b):
    return a + b

def double(x):
    return 2 * x

def foo(**kwargs):
    return kwargs

result = (
    switch(3)
    (1, add, 7, 9)
    (2, double, 5)
    (3, foo, bar=0, spam=8)
    (4, lambda: double(1 / 0))  # if evaluating arguments is not safe
).pick()

print(result)

请注意,这是链接调用,即switch(3)(...)(...)(...)。不要在中间加逗号。把它们放在一个表达式中也很重要,这就是为什么我在隐式行继续的主调用周围使用了额外的括号。

如果您打开一个未处理的值,例如switch(5)(1, ...)(2, ...)(3, ...),上面的示例将引发一个错误。您可以提供默认值,例如switch(5, default=-1)...返回-1


我从python文档中找到了以下最有用的答案:

你可以用一系列的if... elif... elif... else很容易地做到这一点。对于switch语句语法有一些建议,但是对于是否以及如何进行范围测试还没有达成共识。有关完整的详细信息和当前状态,请参阅PEP 275。

对于需要从大量可能性中进行选择的情况,可以创建一个字典,将事例值映射到要调用的函数。例如:

1
2
3
4
5
6
7
8
9
def function_1(...):
    ...

functions = {'a': function_1,
             'b': function_2,
             'c': self.method_1, ...}

func = functions[value]
func()

对于对对象调用方法,可以使用getAttr()内置的检索具有特定名称的方法进一步简化:

1
2
3
4
5
6
7
8
def visit_a(self, ...):
    ...
...

def dispatch(self, value):
    method_name = 'visit_' + str(value)
    method = getattr(self, method_name)
    method()

建议您对方法名使用前缀,例如本例中的visit_。如果没有这样的前缀,如果值来自不受信任的源,攻击者将能够调用对象上的任何方法。


我已经做了一个不太在外部使用ifs的switch-case实现(它仍然在类中使用if)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SwitchCase(object):
    def __init__(self):
        self._cases = dict()

    def add_case(self,value, fn):
        self._cases[value] = fn

    def add_default_case(self,fn):
        self._cases['default']  = fn

    def switch_case(self,value):
        if value in self._cases.keys():
            return self._cases[value](value)
        else:
            return self._cases['default'](0)

像这样使用:

1
2
3
4
5
6
7
8
9
from switch_case import SwitchCase
switcher = SwitchCase()
switcher.add_case(1, lambda x:x+1)
switcher.add_case(2, lambda x:x+3)
switcher.add_default_case(lambda _:[1,2,3,4,5])

print switcher.switch_case(1) #2
print switcher.switch_case(2) #5
print switcher.switch_case(123) #[1, 2, 3, 4, 5]

还可以使用列表存储案例,并通过选择调用相应的函数-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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"

在螺丝台上也解释过


格雷格的解决方案不适用于不可清洗的条目。例如,在索引lists时。

1
2
3
4
5
# doesn't work
def give_me_array(key)
  return {
      [1, 0]:"hello"
    }[key]

幸运的是,虽然tuples是可以散列的。

1
2
3
4
5
# works
def give_me_array(key)
  return {
      (1, 0):"hello"
    }[tuple(key)]

同样,字典或集合的版本也可能是不可变的(因此可能是哈希的)。


当我需要一个简单的开关案例来调用一系列方法,而不只是打印一些文本时,下面的内容适用于我的情况。在玩了lambda和globals之后,我觉得这是迄今为止最简单的选择。也许它也能帮助别人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def start():
    print("Start")

def stop():
    print("Stop")

def print_help():
    print("Help")

def choose_action(arg):
    return {
       "start": start,
       "stop": stop,
       "help": print_help,
    }.get(arg, print_help)

argument = sys.argv[1].strip()
choose_action(argument)()  # calling a method from the given string

还有另一个选择:

1
2
3
4
5
6
7
8
def fnc_MonthSwitch(int_Month): #### Define a function take in the month variable
    str_Return ="Not Found"     #### Set Default Value
    if int_Month==1:       str_Return ="Jan"  
    if int_Month==2:       str_Return ="Feb"  
    if int_Month==3:       str_Return ="Mar"  
    return str_Return;          #### Return the month found  
print ("Month Test 3: " + fnc_MonthSwitch( 3) )
print ("Month Test 14:" + fnc_MonthSwitch(14) )

我只使用if/elif/else语句。我认为它足以取代switch语句。


易于记忆:

1
2
3
4
5
6
7
8
9
10
11
12
13
while True:
    try:
        x = int(input("Enter a numerical input:"))
    except:
        print("Invalid input - please enter a Integer!");
    if x==1:
        print("good");
    elif x==2:
        print("bad");
    elif x==3:
        break
    else:
        print ("terrible");


如果您实际上只是返回一个预先确定的固定值,那么您可以创建一个字典,其中所有可能的输入索引都是键,以及它们对应的值。另外,您可能并不真正希望函数这样做——除非您正在以某种方式计算返回值。

哦,如果你想做些像这样的转换,看这里。


switch语句只是if/elif/else的语法糖。任何控制语句所做的都是基于特定条件委托作业-决策路径。为了将其封装到模块中并能够基于其唯一ID调用作业,可以利用继承和python中的任何方法都是虚拟的这一事实来提供派生类特定的作业实现,如特定的"case"处理程序

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/usr/bin/python

import sys

class Case(object):
   """
        Base class wich specifies the interface for"case" handler.
        The all required arbitrary arguments inside"execute" method will be
        provided through the derived class
        specific constructor

        @note in python, all class methods are virtual
   """

    def __init__(self, id):
        self.id = id

    def pair(self):
       """
            Pairs the given id of the"case" with
            the instance on which"execute" will be called
       """

        return (self.id, self)

    def execute(self):#base class virtual method that needs to be overrided
        pass

class Case1(Case):
    def __init__(self, id, msg):
        self.id = id
        self.msg = msg
    def execute(self):#override the base class method
        print("<Case1> id={}, message: "{}"".format(str(self.id), self.msg))

class Case2(Case):
    def __init__(self, id, n):
        self.id = id
        self.n = n
    def execute(self):#override the base class method
        print("<Case2> id={}, n={}.".format(str(self.id), str(self.n)))
        print("
"
.join(map(str, range(self.n))))



class Switch(object):
   """
        The class wich delegates the jobs
        based on the given job id
   """

    def __init__(self, cases):
        self.cases = cases#dictionary: time complexitiy for access operation is 1
    def resolve(self, id):

        try:
            cases[id].execute()
        except KeyError as e:
            print("Given id: {} is wrong!".format(str(id)))



if __name__ == '__main__':

    # Cases
    cases=dict([Case1(0,"switch").pair(), Case2(1, 5).pair()])

    switch = Switch(cases)

    # id will be dynamically specified
    switch.resolve(0)
    switch.resolve(1)
    switch.resolve(2)

switch语句只是语法上的糖分,这可能是Python没有它的原因。您可以轻松地使用if-else语句来实现此功能。

正如马修·辛克尔所说,你可以使用if和elif等。

像大多数switch语句一样具有"贯穿"功能也是一件简单的事情。你所要做的就是不要使用elif。

1
2
3
4
5
6
if x == 1:
    # 1
if x == 2:
    # fall-through
elif x == 3:
    # not fall-through

1
2
def f(x):    
  return {'a': 1,'b': 2,}.get(x) or"Default"

推荐阅读

    linux命令行输入汉字?

    linux命令行输入汉字?,系统,软件,发行,管理,终端,命令,模式,编辑,文件,文本,L

    linux使用命令改语言?

    linux使用命令改语言?,系统,工作,管理,电脑,设备,字符集,中文,命令,语言,虚

    linux命令被替换文件?

    linux命令被替换文件?,系统,文件,命令,批量,工具,电脑,工作,保险,实时,字符

    c语言写linux命令?

    c语言写linux命令?,系统,工具,代码,智能,工作,环境,情况,位置,命令,文件,如何

    linuxvi替换命令?

    linuxvi替换命令?,档案,位置,命令,工作,底部,模式,编辑,文件,控制台,字符,lin

    linux命令输入格式化?

    linux命令输入格式化?,电脑,设备,系统,管理,位置,环境,名称,数据,磁盘,命令,l

    linux输入上条命令?

    linux输入上条命令?,系统,命令,工作,电脑,数字,时间,管理,工具,第一,网上,lin

    linux输入过的命令?

    linux输入过的命令?,系统,地址,数字,命令,工具,工作,环境,界面,历史,指令,lin

    linux输入命令的地方?

    linux输入命令的地方?,系统,管理,单位,地址,地方,密码,命令,位置,发行,权威,l

    linux汇编语言命令?

    linux汇编语言命令?,系统,地址,代码,数据,网络,平台,平均,位置,灵活,工作,汇

    linux输入过的命令?

    linux输入过的命令?,系统,地址,数字,命令,工具,工作,环境,界面,历史,指令,lin

    linux输入上条命令?

    linux输入上条命令?,系统,命令,工作,电脑,数字,时间,管理,工具,第一,网上,lin

    linux输入命令的地方?

    linux输入命令的地方?,系统,管理,单位,地址,地方,密码,命令,位置,发行,权威,l

    linux命令输入格式化?

    linux命令输入格式化?,电脑,设备,系统,管理,位置,环境,名称,数据,磁盘,命令,l

    linux替换日志命令?

    linux替换日志命令?,服务,系统,软件,代码,信息,文件,日志,首次,可取,工具,lin

    linux汇编语言命令?

    linux汇编语言命令?,系统,地址,代码,数据,网络,平台,平均,位置,灵活,工作,汇

    linux命令替换字符串?

    linux命令替换字符串?,字符串,文件,批量,首次,数据,命令,内容,方法,用字,结

    linux命令输入在那?

    linux命令输入在那?,系统,工具,电脑,命令,终端,发行,工作,管理,密码,下面,lin

    linux输入文本命令?

    linux输入文本命令?,系统,位置,电脑,工作,首开,命令,终端,模式,指令,字符,如