Python 3000 進度報告

翻譯 Python 3000 進度報告(有點長!)

新一篇: Python 3000 FAQ

<script type="text/javascript">function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>
摘要

這是大家期待已久的對Python3000項目最新進展的報告。在過去兩個月的進度中,產生了許多激動人心的新特性。 在接下來的兩個月中,我會分幾次親自來向大家展示這些東西。

項目簡介

早期歷史

最開始冒出 Python 3000 這個想法貌似還是在 2000 年 Python 大會上。 取 Python 3000 這個名字是從 Windows 2000 這個名字中得來的靈感。 很長一段時間以來,Python 已經積累了許多讓我後悔的設計和瑕疵,如果不破壞向後兼容性,根本沒辦法修正它們。 於是我想爲了 Python 日後能夠繼續輕裝上陣,讓 Python 3000 成爲第一個不考慮向後兼容性的版本。

近期歷史

大概一年半以前(並非巧合,因爲那個時候我已經在Google工作了,這讓我可以把比以前多得多的時間花在 Python 上面。) 我覺得是時候要開始實際地設計和規劃 Python 3000 了。 我與 Python 開發者及用戶社區一起整了一個計劃出來。 我們建了一個新的 PEP (Python Enhancement Proposals) 系列, 他們的序號從 3000 開始。 那個時候我們已經有了一個 PEP 3000 了,由社區裏面其他一些人維護的,那上面已經記錄了 許多適合在 Python 3000 中實現的想法清單。 這個 PEP 現在已經更名爲 PEP 3100 。 而 PEP 3000 現在用來描述整個項目的設計 哲學和時間表了。

從那時候起我們不敢說是撼動了一座大山, 但是在郵件列表 python-dev 及其後的 python-3000 的橋洞下面卻着實是有着一股巨流在涌動。

暫定時間表

大約一年前公佈了第一份時間表,那時候我們希望能在2007上半年的末尾發佈第一個3.0 alpha版本, 然後在一年後發佈 3.0 正式版。(Python 3.0 纔是發佈的版本號,"Python 3000" 和 "Py3K" 只是項目的 code name )

這個時間表已經向後推遲了一些;我們現在希望能在8月份結束的時候發佈第一個 alpha 版本。最終版的發佈時間自然也要向後順延了。(這次時間表的推遲主要是因爲過渡到全 Unicode 字符串和 可變的字節數組需要做大量的工作。另外也是因爲我“放權”不夠,沒有把更多的工作交給其他 開發人員來做;這個錯誤我一直在瘋狂地彌補。)

Python 2.6

我們計劃在 3.0 發佈前的幾個月發佈一個 Python 2.6 的姐妹版,發佈這個版本的 4 個月前會先發佈一個 alpha 版本(也就是在第一個 3.0 alpha 版發佈之後)。 下面兩節會解釋這個版本所肩負的任務。 如果你沒興趣跟蹤最新的變化(living on the bleeding edge), 那你完全可以使用 Python 2.6 ,它和 2.5 區別並不大。

兼容性和過渡

兼容性

Python 3.0 會完全地破壞向後兼容性。 我們甚至不期望某個兼容的子集(當然肯定會有這麼一個子集, 只不過我們不打算支持在這個子集中編寫重要程序。它 只不過是從 2.6 到 3.0 恰巧沒變的那些特性的集合罷了)。

Python 2.6 則會完全保持與 Python 2.5 的向後兼容性 (並儘可能保持與更早版本的向後兼容性),而且它還會 以下面幾種方式支持某種意義上的向前兼容性:

  • Python 2.6 會支持一個“Py3k 警告模式”,它會在運行時對那些在 Python 3.0 下無法工作的特性發出警告,比如說把 range() 函數返回 的東西當列表用。
  • Python 2.6 會包含 Py3k 中許多特性的“向後移植版”,可能是通過 __future__ 語句 激活這些特性,或者是允許新舊語法同時存在(只要這個新語法在 2.4 中不是個語法錯誤)。
  • 作爲 2.6 版向前兼容的補充,到時候還會有一個獨立的 代碼轉換工具 。這個工具可以做上下文無關的代碼轉換。舉個(非常簡單的)例子,它能把 apply(f, args) 轉換成 f(*args) 。 不過這個工具並不能做數據流分析和類型推導,所以它會簡單地直接把 apply 當做 舊版本中的一個內置函數來處理。

過渡期開發

對於需要同時支持 Python2.6 和 Python3.0 的項目,我們推薦以下開發模式:

  1. 從卓越的單元測試開始,最好是能幾乎覆蓋整個項目。
  2. 然後將項目移植到 Python 2.6。
  3. 開啓 Py3k 警告模式。
  4. 測試、修改循環直到消除所有警告。
  5. 使用 2to3(譯註:上面提到過的代碼轉換工具)工具將代碼轉換到 3.0 的語法。 不要手動編輯輸出的代碼!
  6. 在 3.0 下測試轉換後的代碼。
  7. 如果發現問題,在針對 Python 2.6 的代碼中做修改,然後跳到第 4 步繼續。
  8. 發佈的時候,分別發佈 2.6 和 3.0 的 tarball (或任何其他你們使用的打包格式)。

轉換工具能夠產生高質量的代碼,而且在許多場合同手動轉換的代碼沒有什麼區別。 不過,我們還是 強烈 建議不要編輯 3.0 的代碼,除非你打算以後對 2.6 版本的代碼只會進行維護而不加入新功能(也就是通常你把 2.6 的代碼移到一個維護分支的時候)。

完成第二步需要花費的功夫和通常遷移到一個新的 Python 版本所需要的差不多。我們正在努力 讓 2.5 到 2.6 的過渡儘可能地流暢。

如果代碼轉換工具和 Python 2.6 的向前兼容特性能夠符合預期目標的話,從第三步到第七步應該不會比 以前一個小版本號的遷移(從Python 2.x 到 2.(x+1))更麻煩。

新特性介紹

如果把新特性全部列在這裏,那就太多了;所以我建議你去看相關的 PEP。 不過,我還是要強調一些我發現很重要也是很多人感興趣和比較有爭議的一些特性。

Unicode,編解碼器和 I/O

我們正在遷移到 Java 處理 Unicode 的模型,那就是:(不可變的)文本字符串就是 Unicode ,而二進制數據則由另一個(可變的)“字節數組”的類型來表達。 另外詞法分析器也對 Unicode 更加友好了:默認的源代碼的編碼將會是 UTF-8, 而且非 ASCII 字母也可以用在標識符裏面了,現在還在進行一些這方面討論,主要集中在標準化、明確非 ASCII 字母表、 是否應該某種程度地支持自右向左的程序幾個問題上。不過標準庫自然還是會一如既往地只使用 ASCII 字符做標識符,並且限制在註釋和字面字符串(string literals)中使用非 ASCII 字母,在單元測試中測試一些 Unicode 特性或者是作者名字的情形除外。

我們將會使用 "..."'...' 來表達字面 Unicode 字符串,而 b"..."b'...' 用來表達字面字節數組。比如, b'abc' 等價於使用表達式 bytes([97, 98, 99]) 創建的字節數組。

我們採用一種和以前稍微不同的編解碼器:在 Python 2 中,編解碼器既可以接受 Unicode 也可以接受8位字節數組作爲輸入,同樣也可以輸出這兩種對象的任何一種,而在 Py3k 中編碼過程總是從 Unicode 字符串到字節數組的轉換,而解碼過程則總是相反的方向。 這意味着我們必須丟棄一些不符合這個模型的編解碼器,比如 rot13,base64 和 bz2 (當然我們仍然支持這些轉換操作,只不過不通過 encode/decode API 了)。

新的 I/O 庫

針對上面的這些變化 I/O 庫自然要做相應的變化了。正好我也早有重寫這個庫的意思, 以前我就想要除掉它對 C stdio 庫的依賴。而現在字節數組和字符串的區別也需要 它的 API 有一個(細微的)變化,the two projects were undertake hand in hand。 在這個新庫中,打開二進制流(使用模式 "rb" 或 "wb" 打開)和文本流 (模式名中不包含 "b" )之間將會有明顯的區別。文本流有一個新屬性,encoding,你可以在 打開流的時候顯式地當參數傳給它;如果沒有指定編碼,就會使用系統默認的編碼( 打開一個存在的文件時也可以猜它的編碼)。

對二進制流的讀操作返回的是字節數組,而對文本流的讀操作返回(Unicode)字符串; 寫操作也類似。給二進制流寫入字符串或者給文本流寫入字節數組都會拋出異常。

除此以外,API 基本還是和以前差不多。同樣會有一個內置的 open() 函數, 新的 I/O 庫在 io 模塊中。這個模塊還包含不同類型流的抽象基類 (抽象基類見下文),一個 StringIO 的新實現,還有一個類似 StringIO 的 BytesIO,它實現了一個二進制流,因此也就只能讀寫字節數組。

print 和 格式化

還有兩個和 I/O 有關的新特性:古老的 print 語句變成了 print() 函數, 而詭異的字符串格式化操作符 % 也被字符串對象的新方法 format() 取代。

把 print 變成函數常常讓人大吃一驚(makes some eyes roll)。但是它確實還是有一些 好處的,比如:這樣可以方便地將使用 print 的代碼重構成(比如說)使用 logging 模塊; 並且 print 的語法一直以來也都存在一些爭議,比如 >>file 和末尾跟一個逗號的獨特 語義。現在由關鍵字參數來完成這些任務,大家都滿意了。

同樣,新的 format() 方法也避免了一些 % 操作符的缺陷,特別是語句 "%s" % xx 是個元組時讓人意外的結果,和許多人經常犯的忘記敲 %(name)s 最後的這個 's' 的失誤。新的格式化字符串使用 {0},{1},{2}, ... 來引用傳給 format() 方法位置參數, 用 {a}, {b}, ... 來引用關鍵字參數。還有其他一些特性如用 {a.b.c} 來訪問對象屬性,甚至可以用 {a[b]} 訪問字典和序列。可以用 {a:8} 來指定轉換字符串的長度,使用這個語法也可以傳遞其他格式化選項。

format() 方法還可以不同的維度進行擴展:通過定義 __format__() 方法,任何類型可以定義 他們自己格式化的方式和解釋格式化參數的方式;你還可以創建自定義的格式化 class ,它可以用來 (比如)自動將局部名字空間當作格式化的參數。

對 class 和類型系統的改變

你可能已經猜到了,"classic classes" 終於退出了歷史舞臺。內置類型 object 成爲新 class 的默認基類。這使得一系列新特性得以實現:

  • 類裝飾器。他們和函數裝飾器非常像::

    @art_deco
    class C:
    ...
  • 函數和方法的定義可以加上“簽名(annotated)”,語言核心本身並不給這些簽名賦予 特殊的含義(只是在內省時會提供這些元數據),不過一些標準庫卻可以;比如, generic function (見下文)可以使用這些元數據。這個新語法可讀性非常好::

    def foobar(a: Integer, b: Sequence) -> String:
    ...
  • 新的 metaclass 語法。以前是在 class 定義體中設置一個變量 __metaclass__ , 現在你得在 class 定義頭中指定一個關鍵字參數,比如::

    class C(bases, metaclass=MyMeta):
    ...
  • 自 定義 class 字典。如果 metaclass 定義了 __prepare__() 方法,那麼在進入 class 定義體之前會調用它,用它返回的對象取代標準的字典,而 class 定義體中的屬性就用這個對象進行存儲。比如說,你可以用這個特性來實現一個 "struct" 類型,因爲對於 "struct" 來說屬性定義的 順序是非常關鍵的。

  • 你可以動態地指定基類,比如::

    bases = (B1, B2)
    class C(*bases):
    ...
  • 在 class 定義頭中還可以使用其他的關鍵字參數;這些參數都被傳給 metaclass 的 __new__ 方法。

  • 你可以通過定義 __instancecheck__()__subclasscheck__() 方法 重載相應的 __isinstance__()__issubclass__() 語義。 如果定義了這兩個方法, isinstance(x, C) 等價於 C.__instancecheck__(x) ,而 issubclass(D, C) 等價於 C.__subclasscheck__(D)

  • 抽象基類(Abstract Base Classes, ABC)。如果你想要定義一個 class ,它的實例 的行爲類似 mapping (舉個例子),你就可以繼承 class abc.Mapping 。 一方面,這個 class 提供 mix-in 的行爲,可以提供大部分 UserDictDictMixin 的功能。另一方面,系統地使用這種 ABC 能夠幫助大型框架減少猜測 並獲得正確的行爲:在 Python 2 中,要判斷一個實現了 __getitem__() 方法的對象究竟是序列(sequence)還是映射(mapping),還真不是那麼簡單的事情。 我們提供了下面這些ABC:Hashable, Iterable, Iterator, Sized, Container, Callable; Set, MutableSet; Mapping, MutableMapping; Sequence, MutableSequence; Number, Complex, Real, Rational, Integer。 io 模塊 也定義了許多ABC,所以在 python 歷史上,終於對以前定義模糊的“file-like”這個概念 第一次有了明確的規範。ABC 框架的強大能力來源於(從 zope interface 中學來的)能夠 註冊某一個 class X ,讓它“虛擬地繼承於”一個 ABC Y,而 X 和 Y 可以由不同的作者編寫 ,也可以出現在不同的包中。(需要澄清的是:如果使用的是虛擬繼承,class Y 的 mix-in 行爲並不會 作用在 class X 上;唯一的效果只是 issubclass(X, Y) 返回 True

  • 抽象基類(ABC)需要確保子類實現了完整的接口,爲了支持抽象基類的定義, 我們引入裝飾器 @abc.abstractmethod 來定義抽象方法(只能用在 metaclass 爲 abc.ABCMeta 或其子類的 class 中)。

  • Generic Functions。是否包含這一特性(PEP 3124 中有描述),其實還不是很確定 ,因爲這個 PEP 的進展越來越慢現在幾乎停滯了。希望它的步伐能夠重新恢復過來。 它支持基於所有參數類型的函數分派(譯註:簡單地說就是靜態語言中的重載的動態語言版本), 而不僅僅是通常的只基於目標對象( self )的分派。

其他重要變化

一些精選!

異常

  • 字符串異常去掉了。
  • 所有異常必須繼承自 BaseException ,最好是直接繼承 Exception
  • 去掉 StandardException
  • 異常不再擁有序列(sequence)的行爲。不過現在他們有了一個屬性 args , 它就是由傳給異常構造函數的參數所組成的序列。
  • 語法: except E, e: 變成 except E as e: ,這使得語句 except E1, E2 不再導致混淆。
  • except 子句中 as 後面的名字在退出 except 子句時會被強制移除。
  • sys.exc_info() 變得多餘了(也許會被去掉):取而代之的是 e.__class__ 是異常 類型, e.__traceback__ 是 traceback 。
  • 如果在 exceptfinally 子句中發生異常,會添加另一個可選的屬性 __context__ ,其值爲前一個異常對象。重拋出異常時,使用 raise E1 from E2 可以明確地設置屬性 __cause__ 的值。
  • 以前的 raise 語法變種 raise E, eraise E, e, tb 已經去掉了。

整數

  • 只會有一個內置的整數類型,它就是 int ,它的行爲就和 Python 2 中的 long 一樣。後綴 'L' 消失。
  • 1/2 會返回 0.5 ,而不像以前那樣返回 0 。(使用 1//2 來返回 0)
  • 表達 8 進制字面量的語法改成了 0o777 ,以免年輕程序員搞糊塗。
  • 二進制字面量:0b101 == 5, bin(5) == '0b101'

使用迭代器(Iterator)或可迭代對象(Iterable)而非列表(list)

  • dict.keys()dict.items() 返回集合(其實是視圖); dict.values() 返回一個可迭代的容器視圖。 iter*() 系列消失。
  • range() 返回以前的 xrange() 返回的對象;而 xrange() 消失。
  • zip(), map(), filter() 返回可迭代對象(和他們在 itertools 中 對應的副本目前的行爲一樣)。

其他

  • reduce() 沒了。這並不意味着我不喜歡高端函數(higher-order functions); 只不過是好像所有使用 reduce() 的代碼使用原始的 for 循環重寫後都便得 更可讀了。(例子)
  • 不過 lambda 繼續存在。
  • 倒單引號(`) 語法,通常可讀性都不好,去掉了(直接使用 repr() 吧),同樣 <> 操作符也 去掉了。(使用 != ,這是對 TOOWIDI 原則一個臭名昭著的違反!)

標準庫的革命

關於對標準庫的變化我不想說太多,因爲這部分要到 3.0a1 發佈之後纔會開始實際的工作。 而且我本人也不會去管它(語言核心就夠我弄的了)。顯然的是我們會除掉許多不支持了的或者 過時了的垃圾模塊(比如許多模塊只適用於 SGI IRIX),另外我們還在努力重命名那些以 CapWords 風格命名的模塊名字,像 StringIO 或者 UserDict。

最後

我前面提到過 lambda 將繼續存活嗎?我仍然不斷收到希望保留 lambda 的請求, 所以我想我得再強調一次。別擔心,這個請求我在一年多前就已經同意了。

演講計劃

在最近的幾個會議上,我會親自講解這些(還有更多的)東西:


原文: http://www.artima.com/weblogs/viewpost.jsp?thread=208549
譯者: 黃毅(http://codeplayer.blogspot.com )
發佈了35 篇原創文章 · 獲贊 15 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章