首頁  >  文章  >  後端開發  >  Python 為什麼不支援 switch 語句?

Python 為什麼不支援 switch 語句?

coldplay.xixi
coldplay.xixi轉載
2020-10-10 17:48:123333瀏覽

Python教學欄位這篇文章裡,我們會聊一聊為什麼 Python 決定不支援 switch 語句。

Python 為什麼不支援 switch 語句?

為什麼想要聊這個主題呢?

主要是因為switch 在其它語言中太常見了,而Python 卻不支持,這樣的獨特性本身就值得關注,而回答這個問題,也能更加看清Python 在程式設計上的理念,了解Python 在語法設計中的決策過程。

本文除了會詳細分析PEP-275 和PEP-3103,還會介紹到Python 最新的發展動態(PEP-622),即可能要引入的模式匹配(pattern matching)語法,相信這個話題會開拓大家的眼界,對switch 文法有更全面的認識。

1、switch 是什麼?

在開始正題之前,我們需要先聊聊 switch 是什麼?

有些同學可能會第一時間想到它…

Python 為什麼不支援 switch 語句?

餵~餵~,麻煩收收心,別總想著遊戲啦,我們要說的是程式語言中的switch 語句。

一般而言,switch 的語法格式如下:

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

使用流程圖來表示,大概是這樣的:

Python 為什麼不支援 switch 語句?

##它的用法不難理解: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?

Python 為什麼不支援 switch 語句?

FAQ 即Frequently Asked Questions 的縮寫,表示常見問題,官方列了27 個常見問題,完整清單在此:mp.weixin.qq.com/s/zabIvt4df…

該文件給了幾個建議,告訴了我們幾個switch/case 的替代方案:

    使用if-elif-else 條件判斷語句
  • 使用字典,將case 值與呼叫的函數映射起來
  • 使用內建getattr() 檢索特定的物件呼叫方法
曾有人提出過一些提案(即PEP -275 和PEP-3103),想給Python 引入switch 語法,然而,對於“

是否以及如何進行靶場測試”,大家沒有達成一致的共識。

靶場測試,即 range test,指的是對武器彈藥的技術性能作各種測試驗證,與藥物的臨床試驗一樣,都是在最終產品交付前的一項關鍵性測試。

官方文件對於「為什麼Python 不引入switch」的解釋,實際上來自Python 之父Guido van Rossum 在PEP-3103 中的意見:

Python 為什麼不支援 switch 語句?

#來源:www.python.org/dev/peps/pe…

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 語句的某種必要性、分析了好幾種備選的實現方案,然而,結局是都被拒絕了。

Python 為什麼不支援 switch 語句?

出處:www.python.org/dev/peps/pe…

那么,我们就先来回顾一下核心开发者们都做出了哪些讨论,看一看如果 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)。

Python 為什麼不支援 switch 語句?

截至 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 為什麼不支援 switch 語句?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除