剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

Python程式設計時光 發表於 2021-10-11
Python

就在前幾天( 2021年10月4日) Python 終於正式釋出了 3.10 版本,看了下這個版本的一些特性,最受關注的應該就是 結構模式匹配 了吧?也就是大家所熟悉的 switch-case ,寫錯了不好意思,是 match-case。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

下邊是最簡單的一個 match-case 的例子,看起來是不是非常的直觀簡潔?

def http_error(status):
    match status:
        case 400:
            print("Bad request")
        case 404:
            print("Not found")
        case 418:
            print("I'm a teapot")
        case _:
            print("Something's wrong with the internet")

對這個功能滿懷期待的我,趕緊就下載升級了 3.10 的 Python 趕緊試用,可沒想到在我深入的體驗過後,我從最開始的期待,變成了敬畏。

敬畏,是因為這樣一個看似簡單的新功能,卻有著不少的學習成本,並且對 結構模式匹配 半知半解的人來說,會增大程式碼出錯的概率,並不是大數人都能輕鬆駕馭的。

我為什麼會這麼說呢?我會在文章最後來簡述我的觀點。

鑑於大多數人,都沒有實際用過這種 結構模式匹配,我會從 升級 3.10 開始教大家如何嘗鮮這個新功能,然後我會詳細的介紹 match-case 的使用方法。

1. 升級 3.10 新版本

我本機的電腦上目前的 Python 版本是 3.9.1 的

$ /usr/local/bin/python3 --version
Python 3.9.1

由於這邊我使用的是 mac,因此我從官網上下載的是 Python 3.10 的 pkg 檔案,如果是 win 的使用者,可以下載相應的 msi 或者 exe 檔案。

下載連結我貼在下邊,可以直接訪問下載

mac: https://www.python.org/ftp/python/3.10.0/python-3.10.0-macos11.pkg
win: https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe

我下載好安裝檔案後,雙擊安裝,之後就雙擊下載的 pkg 檔案,進入安裝流程

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

一路點選繼續,該同意的同意一下,出現如下提示表示安裝成功。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

再次在終端上確認下是否升級成功

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

2. or 模式的使用

在上面我已經貼出一個 match-case 的最簡單示例,這邊就直接跳過簡單示例,來說說那些比較特殊的用法。

在 Python 3.10 中其實有新增一個 聯合型別操作符 | ,但這個只能用於型別,具體的用法,我會在下一篇文章中做詳細的介紹,本篇文章還是集中於 match-case 的使用。

在學習match-case 的時候,你會發現,也有一個類似於聯合型別操作符的用法,但請你要注意區別,它並不是聯合型別操作,而是在 match-case 下獨有的 or模式操作符 | ,它可以將多個具體相同邏輯的 case 語句簡寫成同一個

match status:
    case 401 | 403 | 404:
        print("Not allowed")
    case _:
        print("Something's wrong with the internet")

3. 萬用字元匹配任意物件

match-case 的出現有利於提高程式碼的可讀性,讓你的程式碼更加優雅,但同時要使用好它,也是有一些門檻的,特別是萬用字元的使用。

下邊我舉一些例子來進行講解

在如下程式碼中,使用了萬用字元 _ 和 可變引數中的 * 符號

import sys

match sys.argv[1:]:
    case ["quit"]:
        print("exit")
    case ["create", user]:     # 建立單個使用者
        print("create", user)
    case ["create", *users]:  # 批量建立多個使用者
        for user in users:
            print("create", user)
    case _:
        print("Sorry, I couldn't understand the argv")

最後一個 case 中的 _ 並不作為變數名,而表示一種特殊的模式,在前面的 case 中都未命中的情況下,該 case 會是最後的保障,能確保命中,它相當於 Go 語言中的 default 分支。

import "fmt"

func main() {
    education := "本科"

    switch education {
    case "博士":
        fmt.Println("我是博士")
    case "研究生":
        fmt.Println("我是研究生")
    case "本科":
        fmt.Println("我是本科生")
    case "大專":
        fmt.Println("我是大專生")
    default:
        fmt.Println("學歷未達標..")
    }
}

4. 使用可變引數 *args

第二個 case 和 第三個 case 非常的像,區別在於第三個 case中 users 前加了個 *,他跟原 Python 函式中的可變引數是一個用法,會匹配列表的多個值。

在該中表示可以從命令列引數中批量建立使用者。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

在 match-case 中相應的 case 若有執行到,對應的變數是會被建立的。比如

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

5. 使用可變引數 **kv

在如下程式碼中,**rest 會匹配到所有的 args 中的 key 和 value

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

6. 長度的匹配方式

若你希望使用 case 僅對物件的長度做一些匹配,可以使用下面這樣的方式

  • [*_] 匹配任意長度的 list;
  • (_, _, *_) 匹配長度至少為 2 的 tuple

7. 類物件的匹配

對於類物件的匹配,下邊這個例子足夠簡單,不再講解。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

8. 匹配要注意順序

在上邊基本介紹完了 match-case 的使用方法,如需更詳細的內容,不如去通讀下 pep 636 的內容。

在文章最開始的時候,我說過開發者應該對這些新特性 心存敬畏,match-case 這樣一個看似簡單的新功能,卻有著不少的學習成本,如果對 結構模式匹配 半知半解的人來說,可能會增大程式碼出錯的概率,並不是大數人都能輕鬆駕馭的。

之所以會這麼說,是因為 match-case 在面對不同的物件,它的匹配的規則也有所不同。

  • 當 match 的物件是一個 list 或者 tuple 的時候,需要長度和元素值都能匹配,才能命中,這就是為什麼下面這個例子走的是第三個 case 而不是第二個 case。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

  • 當 match 的物件是一個 dict 的時候,規則卻有所不同,只要 case 表示式中的 key 在所 match 的物件中有存在,即可命中。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

  • 而當 match 的物件是類物件時,匹配的規則是,跟 dict 有點類似,只要物件型別和物件的屬性有滿足 case 的條件,就能命中。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

因此在寫 match-case 的時候,最大的難點可能就是如何把握這個順序,才能確保你寫的程式碼不會翻車。

我個人總結一些規律,僅供大家參考:

  • list 或者 tuple:應該從不格式到嚴格
  • dict 或者 object:應該從嚴格到不嚴格

在經過半天時間的嚐鮮後,我有了一些自己的理解,分享給大家,不知道我的理解有沒有問題,但我依然建議大家在 充分了解 match-case 的匹配規則 後,再去使用它。

另外,這個功能一出,有許多人表示 終於來了,也有一些人表示 太雞肋了

我對於此事的看法是,match-case 必然有一定的適用場景,但這不意味著 match-case 是必要的,所有的 match-case 都可以換成 if 表示式,但反過來卻不然,if 可以結合 and 和 or 承接 n 個多複雜的組合判斷,但 match-case 卻不行,它只能用於單個物件進行匹配判斷。

但是從一定程度上來說,它有點多餘,而且有一定的上手成本。

那麼對於這樣的一個 新特性,你會用它嗎?


文章最後給大家介紹三個我自己寫的線上文件:

第一個文件PyCharm 中文指南 1.0 文件

花了兩個多月的時間,整理了 100 個 PyCharm 的使用技巧,為了讓新手能夠直接上手,我花了很多的時間錄製了上百張 GIF 動圖,有興趣的前往線上文件閱讀。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

第二個文件PyCharm 黑魔法指南 1.0 文件

系統收錄各種 Python 冷門知識,Python Shell 的多樣玩法,令人瘋狂的 Python 炫技操作,Python 的超詳細進階知識解讀,非常實用的 Python 開發技巧等。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...

第三個文件Python 中文指南 1.0 文件

花了三個月時間寫的一本 適合零基礎入門 Python 的全中文教程,搭配大量的程式碼案例,讓初學者對 程式碼的運作效果有一個直觀感受,教程既有深度又有廣度,每篇文章都會標內容的難度,是基礎還是進階的,可供讀者進行選擇,是一本難得的 Python 中文電子教程。

剛剛,Python 3.10 正式釋出了!我發現了一個可怕的功能...