2019:海象運算子與 Python 3.8——指定運算式 :=#
事件#
2019 年 10 月,Python 3.8 發佈,主要新特性:
- 海象運算子(walrus operator)(PEP 572):
:=指定運算式——if (n := len(a)) > 10: ... - 位置限定參數(PEP 570):
def f(a, b, /, c, d): ...——/之前只能按位置傳入 - f-string
=除錯語法:f"{x=}"同時輸出名稱與值 typing.TypedDict/Literal/Final:型別系統持續擴充
為何重要#
- PEP 572 是史上爭議最大的 PEP:討論過程中社群激烈分裂,甚至導致 Guido van Rossum 宣布辭去 BDFL(2018 年 7 月)——這是 Python 治理結構的歷史轉捩點(此後改由 Steering Council 委員會治理)。
:=讓「指定」第一次成為運算式:Python 自 1989 年以來堅持「指定是陳述式」(對照 C 的=回傳值),28 年後首次讓步——但用:=符號與=明確區隔。
理論與實用原因#
- 理論原因(
:=): - 「指定是運算式」源自 C(1972)傳統,好處是消除「計算一次、判斷一次、使用一次」的重複呼叫:
if (n := len(a)) > 10比n = len(a); if n > 10少一行且不會漏。 - Python 長期拒絕它是為了防 C 的經典錯誤
if (x = 5)(把比較誤寫成指定);海象運算子用不同符號:=保留這個保護——這是「兼得兩者」的折衷設計。 - 理論背景:表達式導向(expression-oriented)程式設計(Rust、ML 系語言的一切皆表達式);Python 選擇了局部採用。
- 理論原因(位置限定
/):參數名是 API 的一部分,但內建函式(如len(obj))的參數名是實作細節;/讓 API 設計者可以宣告「這些參數名不在公共契約內」,未來可自由改名不破壞相容性——這是 API 相容性契約的理論補完(*args限制關鍵字傳入早在 Python 2 已有,/補上對稱的另一半)。 - 實用原因:迴圈中反覆呼叫
len()/regex match 的場景(如while (chunk := f.read(8192)):)是:=的最大受益者;f-string=則來自 print 除錯的日常痛點。
彌補了什麼缺陷#
:=彌補了「指定必須是獨立陳述式、導致計算結果重複呼叫或多一行樣板」的缺陷。/彌補了「參數名被迫成為公共 API、無法重構」的缺陷。- f-string
=彌補了「print 除錯要打兩次變數名」的缺陷。
程式範例#
海象運算子(PEP 572)——前後對比:
# 3.8 之前:計算一次、判斷一次、使用一次——重複呼叫或多一行樣板
n = len(data)
if n > 10:
print(f"太長了: {n}")
# 3.8 之後::= 讓「指定」成為運算式
if (n := len(data)) > 10:
print(f"太長了: {n}")
# 最大受益者:迴圈中的重複呼叫
while (chunk := f.read(8192)): # 讀檔迴圈:一行取代兩行
process(chunk)
# 對照 C 的經典錯誤:if (x = 5) 把比較誤寫成指定
# Python 用不同符號 := 保留這個保護——兼得兩者
位置限定參數(PEP 570)——API 契約的補完:
def positional_only(a, b, /, c, *, d):
return a + b + c + d
positional_only(1, 2, c=3, d=4) # a, b 只能按位置——名字不是公共契約
# positional_only(a=1, b=2, ...) # TypeError
# 實例:內建函式的參數名是實作細節,未來可自由改名不破壞相容性
len(obj) # 實際上是 len(obj, /)——"obj" 不在公共契約內
f-string = 除錯語法:
x = 42
print(f"{x = }") # "x = 42"——名稱與值同時輸出,print 除錯免打兩次
print(f"{x * 2 = }") # "x * 2 = 84"——表達式也行