Python 中 switch/case 的语法等价物是什么?

What is the syntactical equivalent to switch/case in Python?

C/C++、C#、Java、JavaScript 和 Pascal 等编程语言 (Reference) 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结构的特殊语法的伪代码。

了解像 dictionary-lookups 这样的功能等价物,是否存在与上述编程结构等价的纯 句法

TL;DR

截至 Python 3.10.0 (alpha6 released March 30, 2021), Python has an official syntactical equivalent called match.

基本语法是:

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

对于较旧的 Python 版本,如果您不想求助于 if-elif-else,则只有解决方法。 看到这个优秀的 community post 来收集一些。

例子

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

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

重要告示

如果来自支持 switchcase 的语言,您可能已经了解它们的行为。对于 Python,有一些差异需要注意。

  • 案例不落空

    具有 switch-case 语句的语言通常执行 每个 值匹配的情况 - 从上到下。因此,还有第三个语句 - break - 如果你不想失败,可以在 switch-case 构造中使用:

    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.");
    }
    

    在这个例子中,前 两个 案例将被执行,因为第一个案例失败了。如果第二个case里面没有break语句,所有的case,包括默认的,都会被执行。

    在Python中,只有第一个匹配的case被执行。你可以把它想象成每个案例都包含一个隐藏的 break 语句。

  • 变量引用不能作为条件

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

    此代码确实打印出颜色匹配!

    作为 case 条件的裸变量引用将始终匹配。

    无论如何,像 case "red": ...qualified 这样的文字(即虚线)像 case AllColors.red 这样的名字像预期的那样工作——不需要害怕它们。

    所有这些都是如此,因为 Python Software Foundation 并没有决定只是复制另一个无聊的控制流模型,而是要实际实现一个成熟的 模式匹配器 这不仅仅是一个 switch-case 语句。有关更多信息,请参阅下一节。

强大的模式匹配

match - Match ain't case hooey

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 属性:

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)

它实际上会打印出来

Our base color is green

因为 base_color 现在是一个占位符,它被分配了我们 chosen_color 的值。

这种高级模式匹配还有很多用例,Python documentation.

中提到了其中几个有趣的用例

结语

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

如果您只是想自己动手编写程序,将它们部署到您自己的服务器上,或者如果您打算以打包形式而不是纯源代码文件的形式分发您的创作,请为这个新功能提供一个在您的程序中尝试 - 这将是一个好处!

附录

正在尝试 Python 3.10.0

对于 Windows 和 macOS 用户,this page 提供官方安装程序下载。

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 替换为 different version

前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 较少,但是当在各种情况下您有大量代码会降低可读性时,这很有用。