Python中的switch语句的替换?

Michael Schneider 1719 python switch-statement

我想在Python中编写一个函数,它根据输入索引的值返回不同的固定值.

在其他语言中,我会使用switchcase声明,但Python似乎没有switch声明.在这种情况下,推荐的Python解决方案是什么?

Greg Hewgill.. 1412

你可以使用字典:

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

  • 如果性能是一个问题,我建议将dict放在函数之外,因此它不会在每个函数调用上重新构建dict (364认同)
  • 如果找不到x会怎么样? (93认同)
  • @EliBendersky,在这种情况下,使用`get`方法可能比使用`collections.defaultdict`更正常. (50认同)
  • @nick:你可以使用defaultdict (44认同)
  • @Nick,抛出一个异常 - 执行`} .get(x,default)`而不是应该有一个默认值.(注意:这比将默认设置保留在switch语句之外的情况要好得多!) (22认同)
  • 可惜没有开关/案例支持。我只是在使用if语句,因为如果我在很长一段时间后重新修改我的代码,我希望对其他人和我自己保持简单的逻辑(而忘了@Greg Hewgilland和@Nick的出色回答) (3认同)
  • @AndersonGreen:这是一个不同的问题.这个问题询问了返回"固定价值"的问题.如果你想使用`print`,你必须使用不同的解决方案(因为`print`有副作用,整个字典在构造时会被评估). (3认同)
  • 实现切换的字典也可以作为函数的默认值.这样它就像上面评论中提到的Claudiu一样高效,但它仍然是函数的本地.问题是当有人试图传递不打算从外面使用的字典参数时:)生活充满了权衡. (2认同)

Nick.. 1305

如果你想要默认值,你可以使用字典get(key[, default])方法:

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

  • @JM:嗯,显然字典查找不支持堕落.你可以做双字典查找.即'a'和'b'指向answer1,'c'和'd'指向answer2,它们包含在第二个字典中. (13认同)
  • 如果'a'和'b'匹配1,'c'和'd'匹配2怎么办? (11认同)
  • 最好传递一个默认值 (3认同)
  • @Idan:问题是要复制开关.我确定如果我尝试输入奇数值,我也可以破坏这段代码.是的,它会重新创建,但修复起来很简单. (2认同)

Mark Biek.. 367

我一直喜欢这样做

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

从这里

  • 在这种情况下使用lambda可能不是一个好主意,因为每次构建字典时都会调用lambda. (25认同)
  • @slf不,当控制流到达那段代码时,它将构建3个函数(通过使用3个lambdas)然后构建一个字典,将这3个函数作为值,但它们仍然是未调用的(_evaluate_略有模糊)那个背景)起初.然后字典通过`[value]`索引,它将只返回3个函数中的一个(假设`value`是3个键中的一个).此时尚未调用该函数.然后`(x)`用`x`作为参数调用刚刚返回的函数(结果变为`result`).其他2个函数将不会被调用. (21认同)
  • 很棒的方法,结合get()来处理默认值也是我最好的选择 (15认同)
  • 可悲的是,这是最接近人们的.使用`.get()`的方法(就像当前的最高答案一样)需要在调度之前急切地评估所有可能性,因此不仅(不仅非常)非常低效且也不会产生副作用; 这个答案解决了这个问题,但更加冗长.我只想使用if/elif/else,甚至那些和'case'一样长. (13认同)
  • 在所有情况下,每次都不会评估所有函数/ lambdas,即使它只返回其中一个结果? (12认同)
  • @blubberdiblub啊,所以每次都会创建所有的功能,但只评估其中一个 (5认同)
  • 他要求固定价值.为什么在查找时生成一个计算某事的函数?有趣的解决方案,但其他问题. (4认同)

Matthew Schi.. 320

除了字典方法(我非常喜欢BTW)之外,您还可以使用if-elif-else来获取switch/case/default功能:

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

这当然与开关/箱子不同 - 你不能像离开休息那样容易穿透; 声明,但你可以进行更复杂的测试.它的格式比一系列嵌套ifs更好,即使功能上它更接近它.

  • 我真的更喜欢这个,它使用一个标准的语言结构,如果没有找到匹配的情况,就不会抛出KeyError (48认同)
  • 我想到了字典/ [`get`](https://docs.python.org/3/library/stdtypes.html#dict.get)方式,但标准方式更具可读性. (7认同)
  • 不错,"不使用elif的堕落"有点令人困惑.那怎么样:忘记"摔倒"并接受它作为两个`if/elif/else`的? (7认同)
  • 同样值得一提的是,当在“ bc”中使用x时,请记住,“ bc”中的“”是“ True”。 (5认同)
  • @someuser,但事实是它们可以“重叠”是一个功能。您只需要确保顺序是匹配发生的优先顺序即可。至于重复的x:只需在前面做一个x = the.other.thing`。通常,您会有一个if,多个elif和一个else,因为这更容易理解。 (2认同)

ChaimG.. 172

我最喜欢的开关/案例的Python配方是:

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

简单场景简短而简单.

与11行以上的C代码相比:

// C Language version of a simple 'switch/case'.
switch( key ) 
{
    case 'a' :
        result = 1;
        break;
    case 'b' :
        result = 2;
        break;
    default :
        result = -1;
}

您甚至可以使用元组分配多个变量:

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

  • 我发现这是一个比接受的更强大的答案. (15认同)
  • @some user:C要求返回值对于所有情况都是相同的类型.Python没有.我想强调Python的这种灵活性,以防有人出现这种情况需要保证这种情况. (3认同)
  • @some用户:我个人认为{} .get(,)是可读的.为了获得Python初学者的额外可读性,您可能需要使用`default = -1; result = choices.get(key,default)`. (3认同)
  • 比较1行的c ++`result = key =='a'?1:key == b?2:-1` (3认同)
  • @Jasen可能会争辩说,您也可以在一行Python中做到这一点:`result = 1,如果key =='a'else(2,如果key =='b'else'default')。但是那只班轮可读吗? (3认同)

小智.. 100

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))

用法:

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

测试:

n = 2
#Result:
#n is an even number.
#n is a prime number.
n = 11
#Result:
#Only single-digit numbers are allowed.

  • 这不是威胁安全的.如果同时击中多个开关,则所有开关都采用最后一个开关的值. (62认同)
  • 虽然@francescortiz可能意味着线程安全,但它也不是威胁安全的.它威胁着变量的价值! (46认同)
  • 如果在多个功能中使用,这也是不安全的.在给出的例子中,如果`case(2)`块调用另一个使用switch()的函数,那么在执行`case(2,3,5,7)`等寻找下一个要执行的情况时,它会使用由另一个函数设置的开关值,而不是当前switch语句设置的值. (7认同)
  • 线程安全问题可能通过使用[线程本地存储](https://docs.python.org/2/library/threading.html#threading.local)来解决.或者可以通过返回实例并将该实例用于案例比较来完全避免. (6认同)
  • @blubberdiblub然而,使用标准的`if`语句是不是更有效率? (6认同)
  • @wizzwizz4开关可以返回一个函数:`case = switch(foo)`如果比上面的效率更高(或者你的意思是更少打字) (2认同)
  • 这样使用模块全局变量来跟踪局部状态是一个糟糕的主意。当某些切换用例逻辑调用一个也使用`switch`的函数时会发生什么?`switch.value`将被覆盖,导致后续案例的处理不当![下面有这种方法的更好的实现方式](/sf/ask/17360801/),人们应该反对这个答案,而赞成那个答案。 (2认同)

John Doe.. 50

我最喜欢的是一个非常好的食谱.你真的很喜欢它.它是我见过的最接近实际的switch case语句,特别是在功能方面.

这是一个例子:

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

  • 抱歉没有花更多精力来自己确定:上面的类似答案不是线程安全的.这是? (5认同)
  • @Skirmantas:请注意,``with``不允许使用``break``,所以删除了fallthrough选项. (4认同)
  • 我会将`for switch()`替换为`with switch()作为case`,更有意义,因为它只需要运行一次. (3认同)

小智.. 47

我从Twisted Python代码中学到了一种模式.

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,根据我们的案例修改)看起来像这样

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))

  • EVAL?当真?而不是每次调用实例化一个方法,我们可以很好地实例化一次并在所有调用中使用它,前提是它没有内部状态. (8认同)
  • 我没有看到这种模式直接调用方法的好处:SMTP().do_HELO('foo.bar.com')好的,lookupMethod中可以有公共代码,但是因为它也可以被覆盖我看不到你从间接获得的东西. (4认同)

小智.. 46

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

  • 使用上下文管理器是一个很好的创造性解 我建议添加一些解释,并可能链接到关于上下文管理器的一些信息,以给这篇文章一些,好吧,上下文;) (7认同)
  • 我不喜欢if/elif链接很多,但这是我用Python现有语法看到的所有解决方案中最具创造性和最实用的. (2认同)
  • 真的很好 建议的一项改进是在Switch类中添加一个(公共)“ value”属性,以便您可以在语句中引用“ case.value”。 (2认同)

GeeF.. 25

假设你不想只返回一个值,而是想使用改变对象上某些东西的方法.使用此处说明的方法将是:

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

这里发生的是python评估字典中的所有方法.因此,即使您的值为'a',对象也会递增递减x.

解:

func, args = {
  'a' : (obj.increment, (x,)),
  'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))

result = func(*args)

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


user2233949.. 21

我只想把我的两美分放在这里.Python中没有case/switch语句的原因是因为Python遵循'Theres只有一种正确的方法来做某事'的原则.很明显,你可以想出各种方法来重新创建开关/案例功能,但Pythonic实现这一点的方法是if/elif结构.即

if something:
    return "first thing"
elif somethingelse:
    return "second thing"
elif yetanotherthing:
    return "third thing"
else:
    return "default thing"

我觉得PEP 8值得点头.Python的一个美妙之处在于其简洁和优雅.这很大程度上源于我们在PEP 8中提出的原则,包括"只有一种正确的做法"

  • 那么为什么Python有for循环和while循环?使用for循环可以执行的所有操作都可以使用while循环实现. (5认同)

Jeremy Cantr.. 16

扩大"dict as switch"的想法.如果要为交换机使用默认值:

def f(x):
    try:
        return {
            'a': 1,
            'b': 2,
        }[x]
    except KeyError:
        return 'default'

  • 我认为在指定默认值的dict上使用.get()更清楚.我更喜欢在特殊情况下保留Exceptions,它会削减三行代码和一定程度的缩进而不会模糊. (13认同)
  • ****是一种特殊情况.根据有用的情况,它可能是也可能不是罕见的*环境,但它绝对是一个例外(从''默认'`回落)来自规则(从这个词中得到一些东西).通过设计,Python程序可以轻而易举地使用异常.话虽这么说,使用`get`可能会使代码更好一些. (10认同)

小智.. 16

如果您正在搜索extra语句,作为"switch",我构建了一个扩展Python的python模块.它被称为ESPY "Python的增强结构",它可用于Python 2.x和Python 3.x.

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

macro switch(arg1):
    while True:
        cont=False
        val=%arg1%
        socket case(arg2):
            if val==%arg2% or cont:
                cont=True
                socket
        socket else:
            socket
        break

可以像这样使用:

a=3
switch(a):
    case(0):
        print("Zero")
    case(1):
        print("Smaller than 2"):
        break
    else:
        print ("greater than 1")

所以espy在Python中将其翻译为:

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


Asher.. 16

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

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

注意:不要使用"()"的情况下/字典查找内部或它会调用每个函数作为字典/ case块被创建.请记住这一点,因为您只想使用哈希样式查找调用每个函数一次.

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()


leo.. 15

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

switch ...parameter...
case p1: v1; break;
case p2: v2; break;
default: v3;

可以用Python表示如下:

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

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

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

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


JD Graham.. 13

我在谷歌搜索的任何地方找不到我想要的简单答案.但无论如何我都想通了.这真的很简单.决定张贴它,也许可以防止别人头上的一些划痕.关键是"in"和元组.这是带有连贯的switch语句行为,包括RANDOM直通.

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)

规定:

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.

  • 此处,“ Dog”和“ Cat”这两个值均通过SAME功能处理,即它们定义为具有“四只脚”。这是一个抽象词,等同于掉线以及发生中断的SAME case语句处理的不同值。 (2认同)

thomasf1.. 12

我使用的解决方案:

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

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

哪里

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

"lambda x: x - 2"在字典中查找并使用它x=23

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

在字典没有找到它,并使用默认"lambda x: x - 22"x=44.


abarnert.. 12

这里的大部分答案都很古老,尤其是被接受的答案,所以看起来值得更新.

首先,官方Python FAQ涵盖了这一点,并elif为简单案例和dict更大或更复杂的案例推荐链.它还visit_为某些情况建议了一组方法(许多服务器框架使用的样式):

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

常见问题解答还提到了PEP 275,它是为了获得关于添加C风格切换语句的官方一劳永逸的决定而编写的.但是,PEP实际上被推迟到Python 3,并且它仅被正式拒绝作为单独的提案,PEP 3103.当然,答案是否定的,但如果您对原因或历史感兴趣,则两个PEP可以链接到其他信息.


多次出现的一件事(并且可以在PEP 275中看到,即使它被作为实际建议被删除)是如果你真的被8行代码处理4个案件而烦恼,而6您在C或Bash中拥有的行,您可以随时写下:

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

PEP 8并没有完全鼓励这一点,但它具有可读性,而且不太单一.


自从PEP 3103遭到拒绝以来的十多年里,C风格案例陈述的问题,甚至是Go中稍微强大的版本,都被认为是死的; 每当有人提出python-ideas或-dev时,他们都会参考旧的决定.

然而,每隔几年就会出现完全ML风格模式匹配的想法,特别是因为像Swift和Rust这样的语言已经采用了它.问题在于,如果没有代数数据类型,很难在模式匹配中得到充分利用.虽然Guido一直对这个想法表示同情,但是没有人能够提出一个非常适合Python的提案.(你可以阅读我的2014年稻草人的例子.)这可能会改变dataclass3.7和一些零星的提议,以更强大enum的处理和类型,或与各种语句本地绑定的各种提议(如PEP 3150,或目前正在讨论的一系列提案 - -ideas).但到目前为止,它还没有.

对于Perl 6风格的匹配,偶尔也会提出建议,这基本上是从elif正则表达式到单一调度类型切换的所有内容的混搭.


小智.. 10

# 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


Vikhyat Agar.. 10

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


Alejandro Qu.. 9

我做了这个小而干净的解决方案

result = {
    'case1':     foo1, 
    'case2':     foo2,
    'case3':     foo3,
    'default':   default,
}.get(option)()

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


guneysus.. 8

我喜欢Mark Bies的回答

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

我必须跑 results[value](value)

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

  • 只是检查没有人在想"无:"的行为就像`默认:`. (3认同)

emu.. 7

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

简短易读,具有默认值,并支持条件和返回值中的表达式.

但是,它的效率低于带字典的解决方案.例如,Python必须在返回默认值之前扫描所有条件.


Solomon Ucko.. 6

Simple, not tested; each condition is evaluated independently: there is no fall-through, but all cases are evaluated (although the expression to switch on is only evaluated once), unless there is a break statement. For example,

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. (该死!为什么我不能有拖尾的内嵌代码块空白?)如果expression计算结果为1Was 2.如果expression计算结果为2,或者Was something.如果expression计算结果为别的东西。


小智.. 6

您可以使用调度的字典:

#!/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()

输出:

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


William H. H.. 5

定义:

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

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

def sample1(x):
  local = 'betty'
  switch1(x, {
    'a': lambda: print("hello"),
    'b': lambda: (
      print("goodbye," + local),
      print("!")),
    })

我一直试图以一种让我摆脱“ lambda:”的方式重新定义开关,但是放弃了。调整定义:

def switch(value, *maps):
  options = {}
  for m in maps:
    options.update(m)
  if value in options:
    options[value]()
  elif None in options:
    options[None]()

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

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()合并字典。它仍然比我想要的还要难看,但是它具有在表达式上使用哈希查找的基本效率,而不是循环遍历所有键。


J_Zar.. 5

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

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)

可以使用此方法同时使用类作为 "__choice_table"的.通过这种方式,您可以避免异常滥用并保持所有清洁和可测试.

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

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)

因此,复杂性不会在代码流中传播,而是在代码结构中呈现.


小智.. 5

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

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-decorator 一起使用

@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 -module中完成。只需使用pip安装:

pip install NeoPySwitch


Tony Suffolk.. 5

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

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)()

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


Woody1193.. 5

到目前为止,已经有很多答案说:“我们没有在Python中进行切换,可以这样做”。但是,我想指出的是,switch语句本身是一个易于滥用的构造,在大多数情况下可以并且应该避免使用它们,因为它们会促进惰性编程。例子:

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

现在,您可以使用切换语句执行此操作(如果Python提供了一个语句),但是您会浪费时间,因为有些方法可以很好地完成此操作。或者,也许您不那么明显:

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?”。因为这通常是更有趣的问题,并且经常会暴露出您要构建的产品的设计缺陷。

现在,这并不是说也不应该使用任何开关。状态机,词法分析器,解析器和自动机在某种程度上都使用它们,并且通常,当您从对称输入开始并转到非对称输出时,它们可能会有用。您只需要确保不要将开关用作锤子,因为在代码中会看到很多钉子。


归档时间:

查看次数:

1488603 次

最近记录:

7 月,3 周 前