Python中的Switch/Case实现有什么价值?

7 投票
6 回答
4324 浏览
提问于 2025-04-16 14:27

最近,我在网上看到一些讨论,提到在Python中没有一个好的“switch / case”替代方案。我意识到有几种方法可以实现类似的功能——有些用lambda,有些用字典。关于这些替代方案,StackOverflow上也有过其他讨论。甚至还有两个PEP(PEP 0275和PEP 3103)讨论并拒绝将switch / case整合进语言中。

我想出了一个我认为优雅的实现switch / case的方法。

最终的效果是这样的:

from switch_case import switch, case         # note the import style

x = 42
switch(x)                                    # note the switch statement
if case(1):                                  # note the case statement
    print(1)
if case(2):
    print(2)
if case():                                   # note the case with no args
    print("Some number besides 1 or 2")

所以,我的问题是:这个创意值得吗?你们有什么建议可以让它更好?

我把这个包含文件放在了github上,里面有很多示例。(我觉得整个包含文件大约有50行可执行代码,但我有1500行的示例和文档。)我是不是过度设计了这个东西,浪费了很多时间,还是会有人觉得这个有用?

编辑:

我想解释一下为什么这个方法与其他方法不同:
1) 可以有多个路径(执行两个或更多的case),这在字典方法中比较难实现。
2) 可以进行除了“相等”以外的比较检查(比如case(less_than(1000)))。
3) 比字典方法和可能的if/elif方法更易读。
4) 可以追踪有多少个True的case。
5) 可以限制允许多少个True的case。(例如,执行前两个True的case...)
6) 允许有一个默认的case。

这里有一个更详细的示例:

from switch_case import switch, case, between

x=12
switch(x, limit=1)                # only execute the FIRST True case
if case(between(10,100)):         # note the "between" case Function
    print ("%d has two digits."%x)
if case(*range(0,100,2)):         # note that this is an if, not an elif!
    print ("%d is even."%x)       # doesn't get executed for 2 digit numbers,
                                  # because limit is 1; previous case was True.
if case():
    print ("Nothing interesting to say about %d"%x)



# Running this program produces this output:

12 has two digits.

这是一个示例,试图展示switch_case如何比传统的if/else更清晰简洁:

# conventional if/elif/else:
if (status_code == 2 or status_code == 4 or (11 <= status_code < 20) 
          or status_code==32):
    [block of code]
elif status_code == 25 or status_code == 45:
    [block of code]
if status_code <= 100:
    [block can get executed in addition to above blocks]

# switch_case alternative (assumes import already)
switch(status_code)
if case (2, 4, between(11,20), 32):   # significantly shorter!
    [block of code]
elif case(25, 45):
    [block of code]
if case(le(100)):
    [block can get executed in addition to above blocks]

最大的好处在于长长的if语句中,重复了同一个switch很多次。不确定这种用法有多频繁,但似乎在某些情况下这样做是有意义的。

github上的示例文件还有更多的例子。

6 个回答

6

在Stackoverflow上,有很多讨论都在谈论这个问题。你可以在页面顶部使用搜索功能,找找其他的讨论。

不过,我不太明白你的解决方案比一个简单的字典要好在哪里:

def switch(x):
    return {
        1 : 1,
        2 : 2,
    }[x]

虽然,使用这种方法添加一个默认情况并不是一件简单的事。但是,你的例子似乎还是在重复一个复杂的if/else语句,对吧?我不太确定是否需要为这个引入一个外部库。

6

所以,我的问题是:这个东西值得做吗?

不值得。

你有什么建议可以让它更好吗?

有。别费劲了。它到底节省了什么?说真的,你实际上是通过在每个 elif 条件中去掉变量 x,让代码变得更难懂了。而且,把明显的 elif 替换成 if,让所有的 Python 程序员都觉得这些情况是独立的,这样会造成故意的混淆。

这会让人困惑。

最大的节省是在很长的 if 语句中,当同一个开关反复出现时。不太确定这种情况有多常见,但似乎在某些情况下这样做是有道理的。

不,这种情况非常少见,显得很牵强,而且很难阅读。看到实际涉及的变量是非常重要的。省略变量名会让事情变得故意混乱。现在我得去找拥有 switch() 函数的地方,才能理解这个 case

当有两个或更多变量时,这种做法完全崩溃。

3
from pyswitch import Switch   # pyswitch can be found on PyPI

myswitch = Switch()

@myswitch.case(42)
def case42(value):
    print "I got 42!"

@myswitch.case(range(10))
def caseRange10(value):
    print "I got a number from 0-9, and it was %d!" % value

@myswitch.caseIn('lo')
def caseLo(value):
    print "I got a string with 'lo' in it; it was '%s'" % value

@myswitch.caseRegEx(r'\b([Pp]y\w)\b')
def caseReExPy(matchOb):
    print r"I got a string that matched the regex '\b[Pp]y\w\b', and the match was '%s'" % matchOb.group(1)

@myswitch.default
def caseDefault(value):
    print "Hey, default handler here, with a value of %r." % value

myswitch(5)  # prints: I got a number from 0-9, and it was 5!
myswitch('foobar')  # prints: Hey, default handler here, with a value of foobar.
myswitch('The word is Python')  # prints: I got a string that matched the regex '\b[Pp]y\w\b', and the match was 'Python'

你明白这个意思了吧。为什么呢?没错,调度表在Python中是个好办法。我就是觉得每次都写这些东西太麻烦了,所以我写了一个类和一些装饰器来帮我处理这些事情。

撰写回答