99999久久久久久亚洲,欧美人与禽猛交狂配,高清日韩av在线影院,一个人在线高清免费观看,啦啦啦在线视频免费观看www

熱線電話:13121318867

登錄
首頁精彩閱讀python 為什么不支持 switch 語句?
python 為什么不支持 switch 語句?
2020-10-10
收藏

作者:豌豆花下貓 

來源:Python貓

在這篇文章里,我們會聊一聊為什么 Python 決定不支持 switch 語句。為什么想要聊這個話題呢?主要是因為 switch 在其它語言中太常見了,而python卻不支持,這樣的獨特性本身就值得關(guān)注,而回答這個問題,也能更加看清python 在程序設(shè)計上的理念,了解python 在語法設(shè)計中的決策過程。

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

1、switch 是什么?

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

有些同學(xué)可能會第一時間想到它……

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

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

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

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

它的用法不難理解:switch 語句的值滿足哪一個 case 情況,就會執(zhí)行對應(yīng)的代碼塊,執(zhí)行時遇到 break 就跳出,否則就繼續(xù)執(zhí)行下一個 case 分支;一般會在最后放一個 default 分支,作為兜底。

大多數(shù)語言都提供了 switch 語句或者極其相似的東西,例如,在 C/C++/Java /Go 等靜態(tài)語言中,它們都支持 switch-case 結(jié)構(gòu);在 Ruby 中有類似的 case-when 結(jié)構(gòu),在 Shell 語言中,有相似的 case-in 結(jié)構(gòu),在 Perl 中,有 switch-case-else……

switch 語句的好處是支持“單條件多分支”的選擇結(jié)構(gòu),相比 if-else 的二分選擇結(jié)構(gòu),在某些時候會更為簡潔清晰。

但是,在 Python 中,我們看不到 switch-case 或者相近的語法結(jié)構(gòu),這是為什么呢?

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 值與調(diào)用的函數(shù)映射起來
  • 使用內(nèi)置 getattr() 檢索特定的對象調(diào)用方法

曾有人提出過一些提案(即 PEP-275 和 PEP-3103),想給 Python 引入 switch 語法,然而,對于“是否以及如何進行靶場測試”,大家沒有達成一致的共識。

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

官方文檔對于“為什么 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 的主題演講中做了一個快速的民意調(diào)查,結(jié)果表明這個提案沒有得到廣泛的支持。因此,我拒絕了它。

簡而言之,PEP 提案有了,語法實現(xiàn)也有了雛形,但是核心開發(fā)者們似乎沒有達成一致意見,最終導(dǎo)致提案流產(chǎn)了。

3、PEP-275 與 PEP-3103 說了什么?

PEP-3103 是在 2006 年提出的,PEP-275 則是在 2001 年提出的,它們的共同之處是提出了引入 switch 語句的某種必要性、分析了好幾種備選的實現(xiàn)方案,然而,結(jié)局是都被拒絕了。

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

那么,我們就先來回顧一下核心開發(fā)者們都做出了哪些討論,看一看如果 Python 要實現(xiàn) switch 結(jié)構(gòu),會是怎么樣子的?(PS:PEP 里還涉及其它內(nèi)容,本文只摘取與 switch 直接相關(guān)的部分)

PEP-275 提出的語法結(jié)構(gòu)如下:

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

其中 else 分支是可選的,如果沒有它,并且前面的分支都不滿足的話,就什么也不做。另外 case 值 constant 支持不同類型,因為 expr 表達式的類型是動態(tài)的。

PEP-275 還提出讓 switch 不支持掉落(fall-through)行為,即每個 case 分支相互獨立而完整,不用像 C 語言那樣需要寫 break。

該 PEP 還列舉了一些其它的 issue:

  • 重用現(xiàn)有關(guān)鍵字,不引入“switch”和“case”
  • 使用新的關(guān)鍵字,避免與 C 的 switch 概念混淆
  • 支持單分支多值選擇(例如:case 'a', 'b', 'c': …)
  • 還有建議支持范圍取值判斷(例如:case 10..14: …)

除了首選方案,該 PEP 還記錄了幾種風(fēng)格各異的語法方案:

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 的出現(xiàn)做了很好的鋪墊。

那么,我們再來看看由 Guido 編寫的 PEP-3103 說了些什么吧。

它首先認(rèn)可了 PEP-275 中的兩個基礎(chǔ)設(shè)定,例如,實現(xiàn)“隱式的 break”,不讓 case 分支出現(xiàn) fall-through 這種轉(zhuǎn)移控制權(quán)的情況(其它語言似乎都要求顯式地寫 break);else 分支是可選的,復(fù)用 else 關(guān)鍵字,而不用引入“default”。

對于 PEP-275 提倡的那種風(fēng)格,Guido 比較認(rèn)可,但也認(rèn)為它的問題是縮進層次太多,因此建議減少代碼分支縮進的空格數(shù),例如本來縮進 4 空格,改為縮進 2 空格。

PEP-3103 還列舉了另外三種實現(xiàn)方案,分析了它們的差異以及問題,具體內(nèi)容從略,這里只給大家看看它們的風(fēng)格:

# case 分支不縮進
switch EXPR:
case EXPR:
    SUITE
case EXPR:
    SUITE
....
else:
    SUITE

# switch 語句后不加冒號
switch EXPR
case EXPR:
    SUITE
case EXPR:
    SUITE
....
else:
    SUITE

# 省略 case 關(guān)鍵字
switch EXPR:
    EXPR:
        SUITE
    EXPR:
        SUITE
    ...
    else:
        SUITE

在基礎(chǔ)語法之外,Guido 花了很多篇幅來討論擴展語法(Extended Syntax),即在一個 case 分支中實現(xiàn)匹配多個值的復(fù)雜情況:

case EXPR, EXPR, ...:

# Guido 優(yōu)選的
case in EXPR_LIST:

case *EXPR:

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

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

他重點考慮到的問題包括:switch 中表達式的結(jié)果是元組或可迭代對象的情況、case 的值被看成元組解包的情況、在 case 分支作“*”星號操作……

接著,Guido 又用了非常非常多的篇幅來分析該如何實現(xiàn) switch,其中討論到的主要思路有:

  • 使用等價的 if-elif 鏈來定義 switch 語句(可能會做些優(yōu)化)
  • 同上,另外所有表達式都必須是可哈希的(hashable)
  • 看作是預(yù)先計算的字典的分派(dispatch)

PEP 中這部分的內(nèi)容非常多,因為在每個思路上,Guido 還考慮到了好幾種實現(xiàn)路徑,這導(dǎo)致了他在復(fù)雜分析后的結(jié)論是:It is too early to decide( 現(xiàn)在做決定為時尚早)。

閱讀完 PEP-3103 后,我總體的感覺是:Guido 的思路非常發(fā)散、層次豐富,但是,缺少了他在面對其它問題時那“快刀斬亂麻”式的洞察力。

也就是說,在諸多的可能性方案中,他力求面面俱到,最終無法說服自己做出一個獨裁的決定。阻力主要來自于他自己,而不是其他人。

不過,之所以會出現(xiàn)這種情況,也許跟他的預(yù)設(shè)立場有關(guān):他似乎認(rèn)為“Python is fine without a switch statement”,因此盡管寫了很長的 PEP,但只是在把問題復(fù)雜化,把議題擱置起來。

最后,他在 PyCon 上做了一個小范圍調(diào)查,借此“名正言順”地拒絕了自己發(fā)起的 PEP,試圖堵住眾人的悠悠之口……

4、未來會有 switch 語句么?

歸結(jié)起來,之所以 Python 沒有 switch 語句,原因有:switch 的實現(xiàn)細節(jié)/功能點未經(jīng)敲定、沒有 switch 也挺好的、有其它不錯的方法替代 switch、Guido 的小任性……

但是,我們還是要追問一句:未來會有 switch 語句么?或者類似的多分支選擇結(jié)構(gòu)?

為什么要有此一問呢?原因是有太多語言自帶 switch 語句,而且也有很多人嘗試編寫提供 switch 功能的庫(我記得在 PyCoder's Weekly 里曾見到過兩次)。

我(Python貓)本人自始至終并不喜歡 switch,幾乎可以肯定地說,Python 未來也不會有 switch,但是,它很可能會引入一個類似于 switch 且更為復(fù)雜的語法結(jié)構(gòu)!

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

截至 2020 年 10 月,該 PEP 已被分解成另外三個 PEP(634-636),目前都處于草案階段??紤]到核心開發(fā)者們的參與情況以及話題討論的情況,這些提案極有可能會在未來版本(比如正在開發(fā)中的 3.10)中實現(xiàn)。

以一個求平均數(shù)的函數(shù)為例,模式匹配語法可以實現(xiàn)成這樣:

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 結(jié)構(gòu)神似于 switch-case 結(jié)構(gòu),然而它基于模式(pattern)而非表達式(expression),因此有更多待考慮的細節(jié)問題,也有更為廣闊的應(yīng)用空間。

對此話題感興趣的讀者,建議去查閱這幾個新的 PEP。

最后,讓我們回到標(biāo)題中的問題:Python 為什么不支持 switch 語句?

官方文檔的 FAQ 對此問題有一個解答,告訴我們有幾個不錯的替代寫法,同時也留下了一條線索:曾有 PEP 提議引入 switch,只是沒有成功實現(xiàn)。

沿著這條線索,本文拆解了 PEP-275 和 PEP-3103 這兩篇文檔,帶大家看到了 Python 社區(qū)里提出過的風(fēng)格各異的 switch 方案,以及諸多的懸而未決的問題。

最后,我們還關(guān)注到了最新的 PEP-622 的動態(tài),看起來 switch 的“孿生兄弟” match 語法有望引入到 Python 中!switch 話題的討論似乎要終止了,但是另一個更大的話題正在進行中!


想從事業(yè)務(wù)型數(shù)據(jù)分析師,您可以點擊>>>“數(shù)據(jù)分析師”了解課程詳情;

想從事數(shù)據(jù)分析師,您可以點擊>>>“大數(shù)據(jù)就業(yè)”了解課程詳情;

想成為人工智能工程師,您可以點擊>>>“人工智能就業(yè)”了解課程詳情;

想了解Python數(shù)據(jù)分析,您可以點擊>>>“Python數(shù)據(jù)分析師”了解課程詳情;

想了解更多優(yōu)質(zhì)課程,請點擊>>>


數(shù)據(jù)分析咨詢請掃描二維碼

若不方便掃碼,搜微信號:CDAshujufenxi

數(shù)據(jù)分析師資訊
更多

OK
客服在線
立即咨詢
客服在線
立即咨詢
') } function initGt() { var handler = function (captchaObj) { captchaObj.appendTo('#captcha'); captchaObj.onReady(function () { $("#wait").hide(); }).onSuccess(function(){ $('.getcheckcode').removeClass('dis'); $('.getcheckcode').trigger('click'); }); window.captchaObj = captchaObj; }; $('#captcha').show(); $.ajax({ url: "/login/gtstart?t=" + (new Date()).getTime(), // 加隨機數(shù)防止緩存 type: "get", dataType: "json", success: function (data) { $('#text').hide(); $('#wait').show(); // 調(diào)用 initGeetest 進行初始化 // 參數(shù)1:配置參數(shù) // 參數(shù)2:回調(diào),回調(diào)的第一個參數(shù)驗證碼對象,之后可以使用它調(diào)用相應(yīng)的接口 initGeetest({ // 以下 4 個配置參數(shù)為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶后臺檢測極驗服務(wù)器是否宕機 new_captcha: data.new_captcha, // 用于宕機時表示是新驗證碼的宕機 product: "float", // 產(chǎn)品形式,包括:float,popup width: "280px", https: true // 更多配置參數(shù)說明請參見:http://docs.geetest.com/install/client/web-front/ }, handler); } }); } function codeCutdown() { if(_wait == 0){ //倒計時完成 $(".getcheckcode").removeClass('dis').html("重新獲取"); }else{ $(".getcheckcode").addClass('dis').html("重新獲取("+_wait+"s)"); _wait--; setTimeout(function () { codeCutdown(); },1000); } } function inputValidate(ele,telInput) { var oInput = ele; var inputVal = oInput.val(); var oType = ele.attr('data-type'); var oEtag = $('#etag').val(); var oErr = oInput.closest('.form_box').next('.err_txt'); var empTxt = '請輸入'+oInput.attr('placeholder')+'!'; var errTxt = '請輸入正確的'+oInput.attr('placeholder')+'!'; var pattern; if(inputVal==""){ if(!telInput){ errFun(oErr,empTxt); } return false; }else { switch (oType){ case 'login_mobile': pattern = /^1[3456789]\d{9}$/; if(inputVal.length==11) { $.ajax({ url: '/login/checkmobile', type: "post", dataType: "json", data: { mobile: inputVal, etag: oEtag, page_ur: window.location.href, page_referer: document.referrer }, success: function (data) { } }); } break; case 'login_yzm': pattern = /^\d{6}$/; break; } if(oType=='login_mobile'){ } if(!!validateFun(pattern,inputVal)){ errFun(oErr,'') if(telInput){ $('.getcheckcode').removeClass('dis'); } }else { if(!telInput) { errFun(oErr, errTxt); }else { $('.getcheckcode').addClass('dis'); } return false; } } return true; } function errFun(obj,msg) { obj.html(msg); if(msg==''){ $('.login_submit').removeClass('dis'); }else { $('.login_submit').addClass('dis'); } } function validateFun(pat,val) { return pat.test(val); }