2000:Python 2.0 與 list comprehension——循環垃圾回收與列表推導式#
事件#
2000 年 10 月,Python 2.0 發佈,這是 Python 由 BeOpen / PythonLabs(Guido 團隊離開 CNRI 後)發佈的第一版,也是社群提案流程 PEP(Python Enhancement Proposal) 制度化的開始。三大新特性:
- 列表推導式(list comprehension)(PEP 202):
[x*x for x in xs if x > 0] - 循環垃圾回收(cyclic garbage collector)(PEP 205)
- 擴充指定運算子
+=、*=(PEP 203),以及 Unicode 字串型別unicode
為何重要#
- PEP 制度:從 2.0 開始,所有重大變更都要寫 PEP 提案、公開討論、由 BDFL(仁慈的終身獨裁者,即 Guido)裁決——Python 的演進從此有治理結構。
- 列表推導式是 Python 語法史上最成功的添加之一,日後成為「Pythonic」風格的代名詞。
- 循環垃圾回收是記憶體管理從「純引用計數」走向混合式回收的轉捩點。
理論與實用原因#
- 理論原因(列表推導式):源自 SETL(1970) 與 Haskell(1990) 的集合建構式(set comprehension)數學記法。理論上,宣告式「描述要什麼」比命令式「描述怎麼做」更接近數學、更不易出錯——沒有索引變數就沒有 off-by-one 錯誤。
- 理論原因(循環 GC):純引用計數(reference counting,繼承自 1960 年代 Lisp 實作)無法回收循環引用(
a.b = b; b.a = a)。2.0 加入 mark-and-sweep 式的循環偵測器(基於 1960 年 McCarthy 的追蹤式回收理論),與引用計數並存——小物件靠計數即時釋放、循環靠週期性掃描。 - 實用原因:Unicode 型別是為了應對 1990 年代末網際網路時代的多語系文字(ASCII 不夠用);
+=是為了減少x = x + 1的樣板。
彌補了什麼缺陷#
- 列表推導式彌補了
map/filter+lambda組合「可讀性差、嵌套 lambda 難懂」的缺陷(並在日後逐步取代它們)。 - 循環 GC 彌補了引用計數「循環引用永不釋放、記憶體洩漏」的根本缺陷。
程式範例#
列表推導式(PEP 202)——前後對比:
xs = [1, -2, 3, -4, 5]
# 2.0 之前:map + filter + lambda,嵌套 lambda 難懂
positives = map(lambda x: x*x, filter(lambda x: x > 0, xs))
# 2.0:列表推導式——描述「要什麼」,接近數學集合記法 {x² | x ∈ xs, x > 0}
positives = [x*x for x in xs if x > 0] # [1, 9, 25]
循環垃圾回收(PEP 205)——引用計數的天敵:
a = []
b = []
a.append(b) # a 引用 b
b.append(a) # b 引用 a —— 循環引用!
del a, b # 引用計數不為零(互相引用),純引用計數永不回收
# 2.0 的循環偵測器會在週期性掃描中回收這塊記憶體
擴充指定與 Unicode:
count = 0
count += 1 # PEP 203,取代 count = count + 1
s = u"中文" # 2.0 的 unicode 型別:應對網際網路時代的多語系文字