在Python中与switch/case的语法等效物是什么?

43

像C/C++、C#、Java、JavaScript和Pascal这样的编程语言(参考资料)结合了switchcase语句(有时也称为selectinspect),允许您检查一个值是否符合多个条件以执行特定的操作。

my_value = 10;
switch(my_value) {
    case 10:
        print("The number is ten");
    case 2*10:
        print("The number is the double of ten");
    case 100:
        print("The number is one hundred");
    default:
        print("The number is none of 10, 2*10 or 100");
}

伪代码,用来描述switch-case语句特殊的语法结构。

如果了解类似于字典查找这样的功能等效方式,那么是否存在纯粹的语法上等效于上述编程结构的内容?


2
注意:我认为这个问题不是重复的,因为我明确地要求一种仅基于语法的实现控制流结构的方法。由于所有其他热门问题都不允许再添加答案,并且许多人只查找第一个答案 - 而不是阅读所有评论 - 他们可能找不到这个新的官方解决方案。因此,这篇自我回答的文章旨在成为通用问题的新标准“一站式”答案,几乎涵盖了每个方面,以便从初学者到经验丰富的开发人员都能找到他们需要的内容。 - fameman
4
这个回答解决了你的问题吗? Python中switch语句的替代方案? - yivi
不能添加新答案并不意味着需要新问题。社区帖子是可以编辑的,这在我看来比创建本质上是重复的新问题更可取。如果我们为语言中的每个功能更改创建一个新问题和答案,那么SO将变得一团糟,因为对于语言的每个版本,您都必须搜索不同的问题,而不是拥有一个不断更新的来源。感谢您的回答,谢谢您抽出时间写它,但我更喜欢您编辑顶部社区答案。 - Kevin
谢谢你的诚实意见,Kevin。不幸的是,我想我的顶级编辑被拒绝了。不过,对于未来来说,你的方法似乎是正确的 - 谢谢。 - fameman
Python 3.10(2021年版)已经支持此功能。 - Peter Mortensen
2个回答

90

简述

截至Python 3.10.0 (alpha6 发布于2021年3月30日),Python有了一种官方的语法等效形式,称为match

基本语法如下:

match value:
    case condition:
        action(s)
    ...

对于较旧版本的Python,如果您不想使用if-elif-else语句,只有一些变通方法可供选择。请参阅这篇出色的社区帖子以获取其中的一些方法。

示例

my_value = 10
match my_value:
    case 10:
        print("The number is ten")
    case 2*10:
        print("The number is the double of ten")
    case 100:
        print("The number is one hundred")
    case _:
        # this is the default handler if none
        # of the above cases match.
        print("The number is none of 10, 2*10 or 100")

因此,其他涉及解决方法的答案从性能角度来看也不再有效。

重要通知

如果你已经了解了支持 switch 和 case 的语言的行为,那么对于 Python,有一些需要注意的差异。

  • Cases don't fall through

    It's common that languages with switch-case statements execute every case the value matches - from top to bottom. Hence, there is a third statement - break - to be used in switch-case constructs if you don't want to fall through:

    value = 10
    switch (value) {
        case 10:
            print("Value is ten");
        case 2*5:
            print("Value is the double of five");
            break;
        case 20/2:
            print("Value is the half of twenty");
        default:
            print("This is just the default action.");
    }
    

    In this example, the first two cases would be executed because the first case falls through. If there was no break statement inside the second case, all cases, including the default one, would be executed.

    In Python, only the first matching case is being executed. You can think of it like every case would contain a hidden break statement.

  • Variable references don't work as a condition

    base_color = "red"
    chosen_color = "green"
    match chosen_color:
        case base_color:
            print("Yes, it matches!")
    

    This code does actually print that the colors match!

    Bare variable references as case conditions will always match.

    Regardless, literals like case "red": ... and qualified (i.e. dotted) names like case AllColors.red work like expected - no need to be scared of them.

    All of this is the case, because the Python Software Foundation did not decide to just copy another boring control flow model, but to actually implement a fully-fledged pattern matcher which is more than just a switch-case statement. More on this can be found in the next section.

强大的模式匹配

match - 匹配啊,不能太过于简单粗暴

Python Enhancement Proposals (PEP) nos. 634-636中提供了规范和信息

在Python中,match不仅仅是一个简单的开关 - 因此可能得名。它具有特殊功能,如深度占位符和通配符。

通过阅读文档而得到的示例 - 这样您就不必自己去读了:

match point:
    case (0, 0):
        print("Origin")
    case (0, y):
        print("Our current Y position is", y, " now.")

你可以匹配任意嵌套的数据结构,包括占位符。在上面的例子中,我们匹配了一个包含两个项的元组,在第二种情况下,我们使用了一个占位符y,它在匹配时被赋值。

你也可以以非常类似的方式匹配类属性:

class Point:
    x: int
    y: int

def location(point):
    match point:
        case Point(x=0, y=0):
            print("Origin is the point's location.")
        case Point(x=0, y=y):
            print("The point lies on the y axis at a height of", y, "units.")

这也解释了为什么你无法在case条件中匹配单个变量引用:你实际上并不匹配该变量的值,而是引入了一个同名的占位符!因此,如果你要像这样打印chosen_color:
base_color = "red"
chosen_color = "green"
match chosen_color:
    case base_color:
        print("Our base color is", base_color)

实际上会打印出

我们的基础颜色是绿色

因为base_color现在是一个占位符,它被赋予了我们选择的颜色的值。

这种高级模式匹配有更多的用例,在Python文档中提到了一些有趣的例子。

结语

Python 3.10 得到应有的采用还需要时间。Python 3.10.0 预计于2021年10月4日发布稳定版 - 这意味着它可能会包含在 Ubuntu 22.04 及以上版本中。

如果你只想玩一下并编写自己的程序,将其部署到自己的服务器上,或者如果你打算以打包形式而不是裸源代码文件分发你的创作,请在你的程序中尝试这个新功能 - 它会带来好处!

补充

尝试 Python 3.10.0

对于 Windows 和 macOS 用户,此页面提供了官方安装程序下载。

Debian 和 Ubuntu 上,您可以使用非常受欢迎的 "DeadSnakes" 项目 PPA:

sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install python3.10
python3.10 --version

尝试Python 3.10.0而不破坏您的系统

Docker是一种使用Python 3.10的选择,无需任何复杂的设置步骤,并在完全隔离的环境中实现。

docker run -it python:3.10.0a6-alpine

就是这样。随着时间的推移,可能会发布新的alpha或beta版本。那时,您将想要将a6替换为不同的版本


2
我正打算评论说这个问题的时间非常完美(因为alpha版本发布还不到一天),但我注意到你已经自己回答了!很棒的答案,感谢你将它添加到SO Python知识库中。 :) - Pranav Hosangadi
是的,碰巧看到了alpha公告并对这个新的强大功能感到非常兴奋,我认为向SO Python社区介绍它是值得的 - 感谢您的评论。 :) - fameman
match/case的设计令人震惊。那些一直希望有一个类似switch/case的替代方案的用户再次受挫。最让我烦恼的是我们无法对变量(非点分隔的常量)进行匹配,以及高缩进——在match后面的冒号和第二个制表符本可以省略(如果我没记错的话,PEP甚至没有讨论这个被拒绝的想法)。 结构匹配确实很有趣,但我想知道是否有更简洁的方式来集成它,比如使用某种形状对象。 有一件事对我来说是清楚的:这些PEP从未应该以那种形式被接受。 - mara004

12

Python 3.10.0及以下版本的回答

没有。通常根据代码上下文有两种方式:

1. if/elif:

使用if/elif语法,你可以得到最相似的switch case版本:

my_value = 10;
if my_value == 10:
    print("The number is ten")
elif my_value == 2*10:
    print("The number is the double of ten")
elif my_value == 100:
    print("The number is one hundred")
else:
    print("The number is none of 10, 2*10 or 100")

2. 字典查找:

另一种不太常见的方法是创建一个字典,在每个 switch/case 条件语句中分配相应的函数以被调用:

my_value = 10;

def def_action():
    print("The number is none of 10, 2*10 or 100")

def ten_action():
    print("The number is ten")

def double_ten_action():
    print("The number is ten")

def hundred_action():
    print("The number is one hundred")

{
    10: ten_action,
    2*10: double_ten_action,
    100: hundred_action,
}.get(
    my_value,
    def_action # This is the final else, if no match if found
)()

这虽然不太符合Pythonic的风格,但在各种情况下,如果有很多使可读性降低的代码,则仍然很有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接