跳轉到

2021:結構化模式比對與 Python 3.10——match/case 與聯合型別#

事件#

2021 年 10 月,Python 3.10 發佈,主要新特性:

  1. 結構化模式比對(PEP 634/635/636):match ... case——帶捕捉與解構的 switch
  2. 聯合型別新語法(PEP 604):int | str 取代 Union[int, str]
  3. 括號化上下文管理器(PEP 617 之後的語法):多個 with 用括號一行搞定
  4. 更精確的錯誤訊息:指出確切欄位的 traceback

為何重要#

  • match/case 是 Python 30 年來最大的語法新增:這不是 C 的 switch——它是結構化模式比對:可以比對序列、映射、類別結構並同時解構捕捉(case Point(x=0, y=y):)。這是 Python 吸收 ML/Haskell/Erlang(1960–1990)與 Rust(2015)傳統的跨界設計。
  • int | str:PEP 604 用數學的聯合符號 | 取代 typing.Union[...],型別語法更接近型別理論的原貌。
  • 錯誤訊息大改進:3.10 的 traceback 標出確切出錯的表示式——教學與除錯體驗質變。

理論與實用原因#

  • 理論原因(match):
  • 模式比對源自 ML(1978) 與 Haskell(1990) 的代數資料類型(ADT)解構、Erlang(1986) 的訊息比對、Rust(2015) 的 match。理論上,「比對形狀並解構」比「層層 if + 手動索引取值」更安全(不會取錯位)、更接近資料結構的定義本身。
  • Python 沒有 ADT,但 3.10 的 match 支援序列/映射/類別模式——用「結構」而非「型別」比對,是 Python 鴨子型別哲學下的獨特妥協;exhaustiveness 檢查則交由型別檢查器(mypy/pyright)補足。
  • 理論原因(X | Y):型別理論中聯合型別(sum type)的標準記法就是 A + B/A | B;typing.Union[A, B] 是 Java 泛型式的囉嗦寫法。PEP 604 讓型別語法回到理論原貌,且 | 是真運算子(可在執行期對型別物件求值)。
  • 實用原因:JSON/訊息/設定解析(形狀多變的資料)是 match 的最大場景;Union 的冗長讓型別提示在實務中敬而遠之,| 大幅降低摩擦。

彌補了什麼缺陷#

  • match/case 彌補了「多分支 if/elif 難以表達資料形狀、解構與比對分離、易取錯索引」的缺陷。
  • X | Y 彌補了 typing.Union「冗長、不像型別理論、嵌套時可讀性崩壞」的缺陷。
  • 精確錯誤訊息彌補了「traceback 不指出確切表示式、教學除錯困難」的缺陷。

程式範例#

結構化模式比對(PEP 634)——前後對比:

# 3.10 之前:層層 if + 手動索引取值,易取錯位
def handle(cmd):
    if isinstance(cmd, list) and len(cmd) >= 3 and cmd[0] == "move":
        x, y = cmd[1], cmd[2]      # 取錯索引就出錯
        return f"move {x} {y}"
    elif cmd == "quit":
        return "bye"
    return "unknown"

# 3.10 之後:比對形狀並解構——源自 ML/Haskell/Rust 的 match
def handle(cmd):
    match cmd:
        case ["move", x, y]:            # 序列模式:比對 + 解構
            return f"move {x} {y}"
        case ["zoom", factor] | ["scale", factor]:  # 或模式
            return f"zoom {factor}"
        case {"action": "login", "user": name}:     # 映射模式
            return f"login {name}"
        case Point(x=0, y=y):           # 類別模式:比對屬性並捕捉
            return f"on y-axis at {y}"
        case _:
            return "unknown"

聯合型別 X | Y(PEP 604)——型別語法回到理論原貌:

# 3.10 之前:typing.Union 冗長,嵌套時可讀性崩壞
from typing import Union, Optional
def find(id: int) -> Optional[Union[str, int]]: ...

# 3.10 之後:數學的聯合符號 |,是真運算子
def find(id: int) -> str | int | None: ...

括號化上下文管理器與精確錯誤訊息:

# 多個 with 一行搞定(3.9 之前要反括號或巢狀)
with (open("a.txt") as a, open("b.txt") as b):
    ...

# 3.10 的 traceback 標出確切出錯的表示式:
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
#     print(count + name)     ← 確切指出是這個 + 出錯

相關條目#