Manjusaka

Manjusaka

為什麼 Python 的 Type Hint 沒有流行起來

在知乎上看到一個很有意思的問題,為什麼 TypeScript 如此流行,卻少見有人寫帶類型標註的 Python?

雖然我沒忍住在知乎上輸出了答案,但是為了以防萬一,我在博客上擴展,與更新一下

BTW 最近上線真的心力交瘁,寫個文章放鬆下

開始#

其實這個答案很簡單,歷史包袱與 ROI,在了解為什麼有這樣的現象之前,首先我們要去了解 Type Hint 能給我們帶來什麼,然後我們需要去了解 Type Hint 的前世今生

在現在這個時間點(2020.03)來看,Type Hint 能給我們帶來肉眼可見的收益是

  1. 透過 annotation ,配合 IDE 的支持,能讓我們在代碼編輯的時候的體驗更好
  2. 透過 mypy/pytype 等工具的支持,我們能在 CI/CD 流程中去集成靜態類型檢查
  3. 透過 pydantic 以及很多新式框架的支持,我們能夠減少很多重複的工作

可能大家以為從 Python 3.5 引入 PEP 484 開始,Python Type Hint 便已經成熟。但是實際上,這個時間比大家想象的短的多

好了,我們現在要去回顧一下整個 Type Hint 發展史上的關鍵節點

  1. PEP 3107 Function Annotations
  2. PEP 484 Type Hints
  3. PEP 526 Syntax for Variable Annotations
  4. PEP 563 Postponed Evaluation of Annotations

PEP 3107#

如同前面所說,大家最開始認識 Type Hint 的時間應該是 14 年 9 月提出,15 年 5 月通過的 PEP 484 。但是實際上雛形早的多,PEP 484 的語法實際上來自於 06 年提出,3.0 引入的 PEP 3107 所設計的語法,參見 PEP 3107 -- Function Annotations

在 PEP 3107 中,對於這個提案的目標,有這樣一段描述

因為 Python 的 2.x 系列缺乏一種標準的方式來註解函數的參數和返回值,各種工具和庫應運而生以填補這一空白。有些利用了在 "PEP 318" 中引入的裝飾器,而其他則解析函數的 docstring,尋找那裡的註解。
這個 PEP 旨在提供一種單一的、標準的方式來指定這些信息,減少由於到目前為止存在的機制和語法的廣泛變化所造成的困惑。

說人話就是,為了能夠給函數的參數或者返回值添加額外的元信息,大家五花八門各顯神通,有用 PEP 318 裝飾器的,有用 docstring 來做的。社區為了緩解這個現象,決定推出新的語法糖,來讓用戶能夠方便的為參數簽名和返回值添加額外的信息

最後形成的語法如下

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    pass

是不是很眼熟? 沒錯,3107 實際上奠定了後續 Type Hint 的基調

  1. 可標註
  2. 作為 function/method 信息的一部分,可 inspect
  3. runtime

但是新的疑惑就來了,為什麼這個提案經常被人忽略?還是,我們需要放在具體的時間點來看

這個提案提出時間最早可以追溯至 06 年,在 PEP3000 這個可能是 Python 歷史上最著名的提案(即宣告 Python 3 的誕生)中確定在 Python 3 中引入,08 年正式發布

在這個時間點下,3107 面臨著兩個問題:

  1. 在 06-08 這個時間點上,社區最主要的精力都在友 (ji) 好 (lie) 的討 (si) 論 (bi),我們為什麼要 Python 3?以及為什麼我們要遷到 Python 3
  2. 3107 實際上只是告訴大家,你可以標註,你可以方便的獲取標註信息,但是怎麼樣去抽象一個類型的表示,如一個 int 類型的 list ,這種事,還是依靠社區自行發展,換句話說,叫做放養

問題 1,無解,只能依靠時間去慢慢推動。問題 2,促成了 PEP 484 的誕生

PEP 484#

PEP 484 這個提案大家應該都有一定程度上的了解了,在此不再描述提案的具體內容

PEP 484 最大的意義在於, 在繼承了 PEP 3107 奠定的語法和基調之上,將 Python 的類型系統進行了合理的抽象,這也是重要的產物 typing,直到這時,Python 中的 type hint 才有了基本的官方規範,同時達到了基本的可用性,這個時間點是 15 年 9 月(9 月 13,Python 3.5.0 正式 Release)

但是實際上 PEP 484 在這個時間點也只能說基本滿足使用,我來舉幾個被詬病的例子

首先看一段代碼

from typing import Optional

class Node:
    left: Optional[Node]
    right: Optional[Node]

這段代碼實際上很簡單對吧,一個標準的二叉樹節點的描述,但是放在 PEP 484 中,這段代碼暴露出兩個問題

  1. 無法對變量進行標註。如同我前面所說的一樣,PEP 484 本質上是 PEP 3107 的一個擴展,這個時候 hint 的範圍僅限於 function/method ,而在上面的代碼中,在 3.5 時期,我是無法對我的 left 和 right 的變量進行標註的,一個編程語言的基本要素之一的變量,無法被 Type Hint ,那麼一定程度上我們可以說這樣一個 type hint 的功能沒有閉環
  2. 循環引用,字面意義,在社區 / StackOverflow 上如何解決 Type Hint 中的循環引用這個問題,一度讓人十分頭大。社區:What the fuck?

所幸,Python 社區意識到了這個問題,推出了兩個提案來解決這樣的問題

PEP 526#

問題 1 促成了 PEP 526 -- Syntax for Variable Annotations 的誕生,16 年 8 月提出,16 年 9 月被接受。16 年 9 月在 BPO-27985 實現。在我印象裡,這應該是 Python 社區中數的出來的爭議小,接收快,實現快的 PEP 了

在 526 中,Python 正式允許大家對變量進行標註,無論是 class attribute 還是普通的 variable

class Node:
    left: str

這樣是可以的,

def abc():
    a:int = 1

這樣也是可以的

在這個提案的基礎上,Python 官方也推動了 PEP 557 -- Data Classes 的落地,當然這是後話

話說回來,526 只解決了上面的问题 1,沒有解決問題 2,這個事情,將會由 PEP 563 來解決

PEP 563#

為了解決循環引用的問題,Python 引入了 PEP 563 -- Postponed Evaluation of Annotations,17 年 9 月社區提出,17 年 11 月被接受,18 年 1 月在 GH-4390 中實現。

在 563 之後,我們上面的代碼可以這麼寫了

from typing import Optional

class Node:
    left: Optional["Node"]
    right: Optional["Node"]

嗯,484 中的兩個問題,終於被解決了

總結#

以 PEP 563 作為重要分割點,Python 最早在 18 年 1 月之後才初步具備完整的生態和生產可用性,如果考慮 release version,那麼應該是 18 年 6 月,Python 3.7 正式發布之後的事了。

在 Python 3.6/7 之後,社區也才開始圍繞 Type Hint 去構建一套生態體系,

比如利用 PEP 526 來高效的驗證數據格式,參見 pydantic

順帶一提,這貨也是目前很火的一個新型框架(也是我目前最喜歡的一個框架)FastAPI 的根基

各大公司也開始跟進,例如 Google 的 pytype ,微軟推出了 pyright 來提供在 VSCode 上的支持

還有許許多多優秀的如 starlette 這樣庫

直到這時,Python + Type Hint 的真正的威力才開始揮發出來。這樣才開始能回答大家這樣一個問題:“我為什麼要切換到 Type Hint”,我猜在 IDE 裡寫的爽肯定不是一個重要原因

要知道,我們在做技術決策時候,一定是因為這個決策能給我們帶來足夠的 benefit,換句話說,有足夠的 ROI,而不是單純的因為,我們喜歡它

這樣看起來,到現在,滿打滿算一年半不超過兩年的時間。對於一個用戶習慣養成周期來說,這太短了。更何況還有一大堆的 Python 2 代碼在那放著 23333

話說回來,作為對比,TypeScript Release 時間可以上溯至 12 年 10 月,發布 0.8 版本,當時的 TS 應該是具備了相對完整地類型系統。

TS 用了 8 年,Python 可能也還有很長的路要走

當然,這個答案也只是從技術和歷史的角度聊聊這個問題。至於其餘的很多因素,包括社區的博奕與妥協等,暫還不在這個答案的範圍內,大家有興趣的話,可以去 python-idea,python-dev,discuss-python 這幾個地方去找一找歷史上關於這幾個提案的討論,非常有意思。

最後,TS 成功還有一個原因,它有個好爸爸 && 它爸爸有錢(逃

嗯,差不多就這樣吧,最近幹活幹的心裡憔悴的我,也就只能寫點垃圾水文了壓壓驚,平復心情了。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。