乐趣区

关于python:Python-为什么不支持-switch-语句

本文出自“Python 为什么”系列,请查看全副文章

在这篇文章里,咱们会聊一聊为什么 Python 决定不反对 switch 语句。

为什么想要聊这个话题呢?

次要是因为 switch 在其它语言中太常见了,而 Python 却不反对,这样的独特性自身就值得关注,而答复这个问题,也能更加看清 Python 在程序设计上的理念,理解 Python 在语法设计中的决策过程。

本文除了会详细分析 PEP-275 和 PEP-3103,还会介绍到 Python 最新的倒退动静(PEP-622),即可能要引入的模式匹配(pattern matching)语法,置信这个话题会宽阔大家的眼界,从而对 switch 语法有更为全面的意识。

1、switch 是什么?

在开始正题之前,咱们须要先聊聊 switch 是什么?

有些同学可能会第一工夫想到它……

喂~ 喂~,麻烦收收心,别总想着游戏啦,咱们要说的是编程语言中的 switch 语句。

一般而言,switch 的语法格局如下:

switch(expression){
    case value1:
       // 语句
       break; // 可选
    case value2:
       // 语句
       break; // 可选
    default: // 可选
       // 语句
}

应用流程图来示意,大略是这样的:

它的用法不难理解:switch 语句的值满足哪一个 case 状况,就会执行对应的代码块,执行时遇到 break 就跳出,否则就继续执行下一个 case 分支;个别会在最初放一个 default 分支,作为兜底。

大多数语言都提供了 switch 语句或者极其类似的货色,例如,在 C/C++/Java /Go 等动态语言中,它们都反对 switch-case 构造;在 Ruby 中有相似的 case-when 构造,在 Shell 语言中,有类似的 case-in 构造,在 Perl 中,有 switch-case-else……

switch 语句的益处是反对“单条件多分支”的抉择构造,相比 if-else 的二分抉择构造,在某些时候会更为简洁清晰。

然而,在 Python 中,咱们看不到 switch-case 或者相近的语法结构,这是为什么呢?

2、Python 为什么不反对 switch?

官网文档中有一篇 FAQ 蕴含了这个问题:Why isn’t there a switch or case statement in Python?

FAQ 即 Frequently Asked Questions 的缩写,示意常见问题,官网列了 27 个常见问题,残缺清单在此:https://mp.weixin.qq.com/s/zabIvt4dfu_rf7SmGZXqXg

该文档给出了几个倡议,通知了咱们几个 switch/case 的代替计划:

  • 应用 if-elif-else 条件判断语句
  • 应用字典,将 case 值与调用的函数映射起来
  • 应用内置 getattr() 检索特定的对象调用办法

曾有人提出过一些提案(即 PEP-275 和 PEP-3103),想给 Python 引入 switch 语法,然而,对于“ 是否以及如何进行靶场测试 ”,大家没有达成统一的共识。

靶场测试,即 range test,指的是对武器弹药的技术性能作各种测试验证,与药物的临床试验一样,都是在最终产品交付前的一项关键性测试。

官网文档对于“为什么 Python 不引入 switch”的解释,实际上来源于 Python 之父 Guido van Rossum 在 PEP-3103 中的意见:

出处:https://www.python.org/dev/peps/pep-3103/

A quick poll during my keynote presentation at PyCon 2007 shows this proposal has no popular support. I therefore reject it.

我在 PyCon 2007 的主题演讲中做了一个疾速的民意调查,结果表明这个提案没有失去宽泛的反对。因而,我回绝了它。

简而言之,PEP 提案有了,语法实现也有了雏形,然而外围开发者们仿佛没有达成一致意见,最终导致提案流产了。

3、PEP-275 与 PEP-3103 说了什么?

PEP-3103 是在 2006 年提出的,PEP-275 则是在 2001 年提出的,它们的共同之处是提出了引入 switch 语句的某种必要性、剖析了好几种备选的实现计划,然而,终局是都被回绝了。

出处:https://www.python.org/dev/peps/pep-0275/

那么,咱们就先来回顾一下外围开发者们都做出了哪些探讨,看一看如果 Python 要实现 switch 构造,会是怎么样子的?(PS:PEP 里还波及其它内容,本文只摘取与 switch 间接相干的局部)

PEP-275 提出的语法结构如下:

switch EXPR:
    case CONSTANT:
        SUITE
    case CONSTANT:
        SUITE
    ...
    else:
        SUITE

其中 else 分支是可选的,如果没有它,并且后面的分支都不满足的话,就什么也不做。另外 case 值 constant 反对不同类型,因为 expr 表达式的类型是动静的。

PEP-275 还提出让 switch 不反对掉落(fall-through)行为,即每个 case 分支互相独立而残缺,不必像 C 语言那样须要写 break。

该 PEP 还列举了一些其它的 issue:

  • 重用现有关键字,不引入“switch”和“case”
  • 应用新的关键字,防止与 C 的 switch 概念混同
  • 反对单分支多值抉择(例如:case ‘a’, ‘b’, ‘c’: …)
  • 还有倡议反对范畴取值判断(例如:case 10..14: …)

除了首选计划,该 PEP 还记录了几种风格各异的语法计划:

case EXPR:
    of CONSTANT:
        SUITE
    of CONSTANT:
        SUITE
    else:
        SUITE

case EXPR:
    if CONSTANT:
         SUITE
    if CONSTANT:
        SUITE
    else:
        SUITE

when EXPR:
    in CONSTANT_TUPLE:
        SUITE
    in CONSTANT_TUPLE:
        SUITE
    ...
else:
     SUITE

PEP-275 记录下了不少重要的思路和问题,为 PEP-3103 的呈现做了很好的铺垫。

那么,咱们再来看看由 Guido 编写的 PEP-3103 说了些什么吧。

它首先认可了 PEP-275 中的两个根底设定,例如,实现“隐式的 break”,不让 case 分支呈现 fall-through 这种转移控制权的状况(其它语言仿佛都要求显式地写 break);else 分支是可选的,复用 else 关键字,而不必引入“default”。

对于 PEP-275 提倡的那种格调,Guido 比拟认可,但也认为它的问题是缩进档次太多,因而倡议缩小代码分支缩进的空格数,例如原本缩进 4 空格,改为缩进 2 空格。

PEP-3103 还列举了另外三种实现计划,剖析了它们的差别以及问题,具体内容从略,这里只给大家看看它们的格调:

# case 分支不缩进
switch EXPR:
case EXPR:
    SUITE
case EXPR:
    SUITE
....
else:
    SUITE

# switch 语句后不加冒号
switch EXPR
case EXPR:
    SUITE
case EXPR:
    SUITE
....
else:
    SUITE

# 省略 case 关键字
switch EXPR:
    EXPR:
        SUITE
    EXPR:
        SUITE
    ...
    else:
        SUITE

在根底语法之外,Guido 花了很多篇幅来探讨扩大语法(Extended Syntax),即在一个 case 分支中实现匹配多个值的简单状况:

case EXPR, EXPR, ...:

# Guido 优选的
case in EXPR_LIST:

case *EXPR:

case [*]EXPR, [*]EXPR, ...:

case *(EXPR, EXPR, ...):

他重点思考到的问题包含:switch 中表达式的后果是元组或可迭代对象的状况、case 的值被看成元组解包的状况、在 case 分支作“*”星号操作……

接着,Guido 又用了十分十分多的篇幅来剖析该如何实现 switch,其中探讨到的次要思路有:

  • 应用等价的 if-elif 链来定义 switch 语句(可能会做些优化)
  • 同上,另外所有表达式都必须是可哈希的(hashable)
  • 看作是事后计算的字典的分派(dispatch)

PEP 中这部分的内容十分多,因为在每个思路上,Guido 还思考到了好几种实现门路,这导致了他在简单剖析后的论断是:It is too early to decide(当初做决定为时尚早)。

浏览完 PEP-3103 后,我总体的感觉是:Guido 的思路十分发散、档次丰盛,然而,短少了他在面对其它问题时那“一刀两断”式的洞察力。

也就是说,在诸多的可能性计划中,他力求八面玲珑,最终无奈压服本人做出一个独裁的决定。阻力次要来自于他本人,而不是其他人。

不过,之所以会呈现这种状况,兴许跟他的预设立场无关:他仿佛认为“Python is fine without a switch statement”,因而只管写了很长的 PEP,但只是在把问题复杂化,把议题搁置起来。

最初,他在 PyCon 上做了一个小范畴考察,借此“名正言顺”地回绝了本人发动的 PEP,试图堵住众人的悠悠之口……

4、将来会有 switch 语句么?

归纳起来,之所以 Python 没有 switch 语句,起因有:switch 的实现细节 / 性能点未经敲定、没有 switch 也挺好的、有其它不错的办法代替 switch、Guido 的小任性……

然而,咱们还是要诘问一句: 将来会有 switch 语句么?或者相似的多分支抉择构造?

为什么要有此一问呢?起因是有太多语言自带 switch 语句,而且也有很多人尝试编写提供 switch 性能的库(我记得在 PyCoder’s Weekly 里曾见到过两次)。

我(Python 猫)自己从头至尾并不喜爱 switch,简直能够必定地说,Python 将来也不会有 switch,然而,它很可能会引入一个相似于 switch 且更为简单的语法结构!

2020 年 6 月,PEP-622 被提出了,它倡议引入在 Scala、Erlang 和 Rust 等语言中的模式匹配语法(pattern matching)。

截至 2020 年 10 月,该 PEP 已被分解成另外三个 PEP(634-636),目前都处于草案阶段。思考到外围开发者们的参加状况以及话题探讨的状况,这些提案极有可能会在将来版本(比方正在开发中的 3.10)中实现。

以一个求平均数的函数为例,模式匹配语法能够实现成这样:

def average(*args):
    match args:
        case [x, y]:           # captures the two elements of a sequence
            return (x + y) / 2
        case [x]:              # captures the only element of a sequence
            return x
        case []:
            return 0
        case x:                # captures the entire sequence
            return sum(x) / len(x)

match-case 构造神似于 switch-case 构造,然而它基于模式(pattern)而非表达式(expression),因而有更多待思考的细节问题,也有更为广大的利用空间。

对此话题感兴趣的读者,倡议去查阅这几个新的 PEP。

最初,让咱们回到题目中的问题:Python 为什么不反对 switch 语句?

官网文档的 FAQ 对此问题有一个解答,通知咱们有几个不错的代替写法,同时也留下了一条线索:曾有 PEP 提议引入 switch,只是没有胜利实现。

沿着这条线索,本文拆解了 PEP-275 和 PEP-3103 这两篇文档,带大家看到了 Python 社区里提出过的风格各异的 switch 计划,以及诸多的悬而未决的问题。

最初,咱们还关注到了最新的 PEP-622 的动静,看起来 switch 的“孪生兄弟”match 语法无望引入到 Python 中!switch 话题的探讨仿佛要终止了,然而另一个更大的话题正在进行中!

本文属于“Python 为什么”系列(Python 猫出品),该系列次要关注 Python 的语法、设计和倒退等话题,以一个个“为什么”式的问题为切入点,试着展示 Python 的迷人魅力。所有文章将会归档在 Github 上,欢送大家给颗小星星,我的项目地址:https://github.com/chinesehua…

退出移动版