Python 中 switch 语句的替代品?

2024-11-19 08:38:00
admin
原创
8
摘要:问题描述:我想用 Python 编写一个函数,根据输入索引的值返回不同的固定值。 在其他语言中我会使用switchorcase语句,但 Python 似乎没有该switch语句。在这种情况下,推荐的 Python 解决方案是什么?解决方案 1:Python 3.10 (2021) 引入了match-case语...

问题描述:

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

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


解决方案 1:

Python 3.10 (2021) 引入了match-case语句,它为 Python 提供了“switch”的一流实现。例如:

def f(x):
    match x:
        case 'a':
            return 1
        case 'b':
            return 2
        case _:
            return 0   # 0 is the default case if x is not found

match-语句的功能case比这个简单的例子强大得多。

文档:

  • match语句(在“更多控制流工具”页面下)

  • match语句(在“复合语句”页面下)

  • PEP 634 –结构模式匹配:规范

  • PEP 636 –结构模式匹配:教程


如果需要支持 Python ≤ 3.9,请改用字典:

def f(x):
    return {
        'a': 1,
        'b': 2,
    }.get(x, 0)  # default case

解决方案 2:

如果您喜欢默认值,您可以使用字典get(key[, default])功能:

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

解决方案 3:

我一直喜欢这样做

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

从这里

解决方案 4:

除了字典方法(顺便说一句,我非常喜欢)之外,您还可以使用- if-elifelse获取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

这当然与 switch/case 并不相同 - 您不能像省略break语句那样轻松地进行 fall-through,但您可以进行更复杂的测试。它的格式比一系列嵌套的 s 更好if,尽管从功能上讲它更接近于嵌套的 s。

解决方案 5:

Python >= 3.10

哇,Python 3.10+ 现在有一个match/case语法,它类似于switch/case甚至更多!

PEP 634——结构模式匹配

精选特色match/case

1 - 匹配值:

匹配值与其他语言中的简单匹配类似switch/case

match something:
    case 1 | 2 | 3:
        # Match 1-3.
    case _:
        # Anything else.
        # 
        # If `case _:` is omitted, an error will be thrown
        # if `something` doesn't match any of the patterns.

2 - 匹配结构模式:

match something:
    case str() | bytes():  
        # Match a string like object.
    case [str(), int()]:
        # Match a `str` and an `int` sequence 
        # (A sequence can be a `list` or a `tuple` but not a `set` or an iterator). 
    case [_, _]:
        # Match a sequence of 2 variables.
        # To prevent a common mistake, sequence patterns don’t match strings.
    case {"bandwidth": 100, "latency": 300}:
        # Match this dict. Extra keys are ignored.

3 - 捕获变量

解析一个对象;将其保存为变量:

match something:
    case [name, count]
        # Match a sequence of any two objects and parse them into the two variables.
    case [x, y, *rest]:
        # Match a sequence of two or more objects, 
        # binding object #3 and on into the rest variable.
    case bytes() | str() as text:
        # Match any string like object and save it to the text variable.

在解析可能以多种不同模式出现的数据(例如 JSON 或 HTML)时,捕获变量非常有用。

捕获变量是一项功能。但这也意味着您只能使用带点的常量(例如COLOR.RED:)。否则,常量将被视为捕获变量并被覆盖。

更多示例用法:

match something:
    case 0 | 1 | 2:
        # Matches 0, 1 or 2 (value).
        print("Small number")
    case [] | [_]:
        # Matches an empty or single value sequence (structure).
        # Matches lists and tuples but not sets.
        print("A short sequence")
    case str() | bytes():
        # Something of `str` or `bytes` type (data type).
        print("Something string-like")
    case _:
        # Anything not matched by the above.
        print("Something else")

Python <= 3.9

我最喜欢的 Python switch/case 方法是:

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

解决方案 6:

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.

解决方案 7:

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

解决方案 8:

我最喜欢的是一份非常棒的食谱。这是我见过的最接近实际 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

以下是一个例子:

# 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!"

一些评论指出,使用with foo as case而不是的上下文管理器解决方案for case in foo可能更简洁,对于大型 switch 语句,线性行为而不是二次行为可能是一个不错的选择。这个带有 for 循环的答案的部分价值在于能够中断和失败,如果我们愿意稍微调整一下关键字的选择,我们也可以在上下文管理器中获得这一点:

class Switch:
    def __init__(self, value):
        self.value = value
        self._entered = False
        self._broken = False
        self._prev = None

    def __enter__(self):
        return self

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

    def __call__(self, *values):
        if self._broken:
            return False
        
        if not self._entered:
            if values and self.value not in values:
                return False
            self._entered, self._prev = True, values
            return True
        
        if self._prev is None:
            self._prev = values
            return True
        
        if self._prev != values:
            self._broken = True
            return False
        
        if self._prev == values:
            self._prev = None
            return False
    
    @property
    def default(self):
        return self()

以下是一个例子:

# Prints 'bar' then 'baz'.
with Switch(2) as case:
    while case(0):
        print('foo')
    while case(1, 2, 3):
        print('bar')
    while case(4, 5):
        print('baz')
        break
    while case.default:
        print('default')
        break

解决方案 9:

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

解决方案 10:

我只是想在这里说说我的看法。Python 中没有 case/switch 语句的原因是 Python 遵循“只有一种正确方法可以做某事”的原则。因此,显然您可以想出各种方法来重新创建 switch/case 功能,但实现这一点的 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 中列出的原则,包括“做某事只有一种正确的方法”。

解决方案 11:

运行函数的解决方案:

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

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

示例 1(带参数):

option = number['type']
result = {
    'number':     value_of_int,  # result = value_of_int(number['value'])
    'text':       value_of_text, # result = value_of_text(number['value'])
    'binary':     value_of_bin,  # result = value_of_bin(number['value'])
}.get(option)(value['value'])

示例 2(无参数):

option = number['type']
result = {
    'number':     func_for_number, # result = func_for_number()
    'text':       func_for_text,   # result = func_for_text()
    'binary':     func_for_bin,    # result = func_for_bin()
}.get(option)()

示例 4(仅限值):

option = number['type']
result = {
    'number':    lambda: 10,       # result = 10
    'text':      lambda: 'ten',    # result = 'ten'
    'binary':    lambda: 0b101111, # result = 47
}.get(option)()

解决方案 12:

假设您不想只返回一个值,而是想使用改变对象的方法。使用此处所述方法将是:

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”将求值返回的函数调用。

解决方案 13:

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

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

注意:不要在 case/dictionary 查找中使用“()”,否则它会在创建 dictionary/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()

解决方案 14:

如果您搜索的是 extra-statement,例如“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")

因此,用 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

解决方案 15:

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

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

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

FAQ 还提到了PEP 275,它是为了获得关于添加 C 风格 switch 语句的官方一劳永逸决定而编写的。但该 PEP 实际上被推迟到了 Python 3,并且它只是作为单独的提案PEP 3103被正式拒绝。答案当然是否定的——但如果您对其原因或历史感兴趣,这两个 PEP 有指向更多信息的链接。


有一件事多次出现(可以在 PEP 275 中看到,即使它被删除作为实际建议)是,如果你真的介意用 8 行代码来处理 4 种情况,而不是像 C 或 Bash 那样只需要 6 行,那么你可以这样写:

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 风格的 case 语句问题,甚至是 Go 中稍微强大一些的版本,都被认为已经死了;每当有人在 python-ideas 或 -dev 上提出这个问题时,他们都会提到旧的决定。

但是,每隔几年就会出现完全 ML 样式模式匹配的想法,特别是自从 Swift 和 Rust 等语言采用了它之后。问题是,如果没有代数数据类型,模式匹配就很难得到充分的利用。虽然 Guido 一直赞同这个想法,但没有人提出一个非常适合 Python 的提案。(您可以阅读我 2014 年的稻草人作为示例。)这可能会dataclass在 3.7 中改变,并有一些零星的提案,以提供更强大的功能enum来处理总和类型,或者使用各种不同类型的语句本地绑定的提案(如PEP 3150,或目前正在 -ideas 上讨论的一组提案)。但到目前为止,还没有。

偶尔也会有针对 Perl 6 风格匹配的提案,它基本上是从elif正则表达式到单分派类型切换的所有内容的大杂烩。

解决方案 16:

扩展“字典作为开关”的想法。如果您想为开关使用默认值:

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

解决方案 17:

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

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 版本不是一个语句,而是一个表达式,它可以计算出一个值。

解决方案 18:

我使用的解决方案:

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

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

解决方案 19:

您可以使用分派的字典:

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

解决方案 20:

我在 Google 搜索中找不到我想要的简单答案。但我还是找到了。这真的很简单。决定发布它,也许可以防止别人的头上少受点划痕。关键就是“in”和元组。以下是带有 fall-through 的 switch 语句行为,包括 RANDOM fall-through。

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.

解决方案 21:

在阅读了接受的答案后,我感到很困惑,但这一切都清楚了:

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

此代码类似于:

function(argument){
    switch(argument) {
        case 0:
            return "zero";
        case 1:
            return "one";
        case 2:
            return "two";
        default:
            return "nothing";
    }
}

检查源代码以获取有关字典映射到函数的更多信息。

解决方案 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

解决方案 23:

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

解决方案 24:

我喜欢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

解决方案 25:

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

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

但是效率不如字典方案,比如Python需要扫描所有条件才能返回默认值。

解决方案 26:

简单,未经过测试;每个条件都独立评估:没有 fall-through,但所有情况都会被评估(尽管 switch on 的表达式只被评估一次),除非有 break 语句。例如,

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计算结果为其他内容。

解决方案 27:

到目前为止,有很多答案都说:“Python 中没有 switch,请按照这种方式操作”。但是,我想指出的是,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

现在,您可以使用 switch 语句(如果 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 中切换?”,因为这往往是一个更有趣的问题,而且往往会暴露出你正在构建的东西在设计上的缺陷。

现在,这并不是说永远都不要使用开关。状态机、词法分析器、解析器和自动机都在某种程度上使用它们,一般来说,当你从对称输入开始并转到非对称输出时,它们会很有用;你只需要确保不要将开关用作锤子,因为你在代码中看到一堆钉子。

解决方案 28:

我倾向于使用的一种解决方案也利用了字典:

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

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

解决方案 29:

定义:

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("!")),
    })

我一直试图重新定义 switch,以便摆脱“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() 在查找值之前会合并字典。它仍然比我想要的更丑陋,但它具有在表达式上使用散列查找的基本效率,而不是循环遍历所有键。

解决方案 30:

扩展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模块中完成了。只需使用 pip 安装即可:

pip install NeoPySwitch
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用