Python 2.5 中的新功能

作者:

A.M. Kuchling

本文件解釋了 Python 2.5 中的新功能。Python 2.5 的最終版本計劃於 2006 年 8 月釋出;PEP 356 描述了計劃的釋出日程。Python 2.5 於 2006 年 9 月 19 日釋出。

Python 2.5 中的更改是語言和庫改進的有趣組合。我認為,庫的增強對 Python 使用者社群來說更重要,因為添加了幾個廣泛有用的包。新模組包括用於 XML 處理的 ElementTree (xml.etree)、SQLite 資料庫模組 (sqlite) 以及用於呼叫 C 函式的 ctypes 模組。

語言更改具有中等重要性。增加了一些令人愉快的新功能,但其中大多數不是你每天都會使用的功能。條件表示式最終以一種新穎的語法新增到語言中;請參閱 PEP 308: 條件表示式 一節。新的 'with' 語句將使編寫清理程式碼更容易(PEP 343: 'with' 語句 一節)。現在可以將值傳遞給生成器(PEP 342: 新的生成器功能 一節)。匯入現在可以是絕對匯入或相對匯入(PEP 328: 絕對匯入和相對匯入 一節)。異常處理的一些特殊情況得到了更好的處理(PEP 341: 統一的 try/except/finally 一節)。所有這些改進都是有價值的,但它們是對一個或另一個特定語言功能的改進;它們都不是對 Python 語義的廣泛修改。

除了語言和庫的新增之外,整個原始碼樹還進行了其他改進和錯誤修復。對 SVN 更改日誌的搜尋發現,在 Python 2.4 和 2.5 之間應用了 353 個補丁並修復了 458 個錯誤。(這兩個數字都可能被低估了。)

本文件不試圖成為新功能的完整規範;相反,它使用有用的示例簡要介紹了更改。有關完整詳細資訊,您應始終參閱 https://docs.python.club.tw 上的 Python 2.5 文件。如果您想了解完整的實現和設計原理,請參閱特定新功能的 PEP。

歡迎對本文件提出評論、建議和錯誤報告;請透過電子郵件傳送給作者或在 Python 錯誤跟蹤器中開啟一個錯誤。

PEP 308: 條件表示式

長期以來,人們一直要求有一種編寫條件表示式的方法,這種表示式根據布林值是真還是假來返回 A 值或 B 值。條件表示式允許你編寫一個賦值語句,其效果與以下程式碼相同:

if condition:
    x = true_value
else:
    x = false_value

在 python-dev 和 comp.lang.python 上,關於語法的討論冗長而乏味。甚至進行了一次投票,結果發現大多數投票者希望以某種形式實現條件表示式,但沒有一種語法得到明確多數的偏愛。候選方案包括 C 語言的 cond ? true_v : false_vif cond then true_v else false_v,以及其他 16 種變體。

Guido van Rossum 最終選擇了一種令人驚訝的語法

x = true_value if condition else false_value

求值仍然是惰性的,就像現有的布林表示式一樣,所以求值順序會有點跳躍。中間的 *條件* 表示式首先被求值,*true_value* 表示式只有在條件為真時才被求值。同樣,*false_value* 表示式只有在條件為假時才被求值。

這種語法可能看起來很奇怪,甚至是倒退的;為什麼條件表示式在表示式的 *中間*,而不是像 C 語言的 c ? x : y 那樣放在前面呢?這個決定是透過將新語法應用於標準庫中的模組,並檢視生成程式碼的可讀性來驗證的。在許多使用條件表示式的場景中,一個值似乎是“常見情況”,而另一個值是“異常情況”,僅在不滿足條件時才偶爾使用。條件語法使這種模式更加明顯:

contents = ((doc + '\n') if doc else '')

我將上述語句理解為“通常,*contents* 被賦值為 doc+'\n';有時 *doc* 是空的,在這種特殊情況下,返回一個空字串。”我懷疑我不會經常使用條件表示式,除非有明確的常見情況和不常見情況。

關於語言是否應該要求用括號將條件表示式括起來,曾有一些討論。最終決定 *不* 要求在 Python 語言的語法中強制使用括號,但作為一種風格,我認為你應該始終使用它們。考慮以下兩個語句:

# First version -- no parens
level = 1 if logging else 0

# Second version -- with parens
level = (1 if logging else 0)

在第一個版本中,我認為讀者的眼睛可能會將語句分成“level = 1”、“if logging”、“else 0”,並認為條件決定是否執行對 *level* 的賦值。在我看來,第二個版本讀起來更好,因為它明確了賦值總是執行的,並且在兩個值之間做出選擇。

包含括號的另一個原因是:列表推導式和 lambda 表示式的一些奇怪組合可能看起來像不正確的條件表示式。請參閱 PEP 308 中的一些示例。如果您將條件表示式用括號括起來,就不會遇到這種情況。

參見

PEP 308 - 條件表示式

PEP 由 Guido van Rossum 和 Raymond D. Hettinger 編寫;由 Thomas Wouters 實現。

PEP 309: 部分函式應用

functools 模組旨在包含用於函數語言程式設計的工具。

此模組中的一個有用工具是 partial() 函式。對於以函式式風格編寫的程式,有時你會希望構造現有函式的變體,其中某些引數已填充。考慮一個 Python 函式 f(a, b, c);你可以建立一個新函式 g(b, c),它等同於 f(1, b, c)。這被稱為“部分函式應用”。

partial() 接受引數 (function, arg1, arg2, ... kwarg1=value1, kwarg2=value2)。生成的物件是可呼叫的,所以你可以直接呼叫它來使用填充的引數呼叫 *function*。

這是一個小而真實的例子:

import functools

def log (message, subsystem):
    "Write the contents of 'message' to the specified subsystem."
    print '%s: %s' % (subsystem, message)
    ...

server_log = functools.partial(log, subsystem='server')
server_log('Unable to open socket')

這是另一個例子,來自一個使用 PyGTK 的程式。這裡正在動態構建一個上下文相關的彈出選單。為選單選項提供的回撥是 open_item() 方法的部分應用版本,其中第一個引數已提供。

...
class Application:
    def open_item(self, path):
       ...
    def init (self):
        open_func = functools.partial(self.open_item, item_path)
        popup_menu.append( ("Open", open_func, 1) )

functools 模組中的另一個函式是 update_wrapper(wrapper, wrapped) 函式,它可以幫助你編寫行為良好的裝飾器。update_wrapper() 會將名稱、模組和文件字串屬性複製到包裝函式,以便更容易理解包裝函式內部的回溯。例如,你可以這樣寫:

def my_decorator(f):
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return f(*args, **kwds)
    functools.update_wrapper(wrapper, f)
    return wrapper

wraps() 是一個裝飾器,可以在你自己的裝飾器內部使用,以複製被包裝函式的資訊。上一個示例的另一種版本是:

def my_decorator(f):
    @functools.wraps(f)
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return f(*args, **kwds)
    return wrapper

參見

PEP 309 - 部分函式應用

PEP 由 Peter Harris 提議並編寫;由 Hye-Shik Chang 和 Nick Coghlan 實現,並由 Raymond Hettinger 進行了調整。

PEP 314: Python 軟體包元資料 v1.1

Distutils 添加了一些簡單的依賴支援。setup() 函式現在有 requiresprovidesobsoletes 關鍵字引數。當你使用 sdist 命令構建源分發時,依賴資訊將記錄在 PKG-INFO 檔案中。

另一個新的關鍵字引數是 download_url,它應該設定為包原始碼的 URL。這意味著現在可以查詢包索引中的條目,確定包的依賴項,並下載所需的包。

VERSION = '1.0'
setup(name='PyPackage',
      version=VERSION,
      requires=['numarray', 'zlib (>=1.1.4)'],
      obsoletes=['OldPackage']
      download_url=('http://www.example.com/pypackage/dist/pkg-%s.tar.gz'
                    % VERSION),
     )

Python 包索引 https://pypi.org 的另一項新增強功能是儲存包的源和二進位制歸檔檔案。新的 upload Distutils 命令會將包上傳到儲存庫。

在上傳包之前,你必須能夠使用 sdist Distutils 命令構建分發包。一旦成功,你就可以執行 python setup.py upload 將你的包新增到 PyPI 存檔。你還可以透過提供 --sign--identity 選項來對包進行 GPG 簽名。

包上傳由 Martin von Löwis 和 Richard Jones 實現。

參見

PEP 314 - Python 軟體包元資料 v1.1

PEP 由 A.M. Kuchling、Richard Jones 和 Fred Drake 提出並撰寫;由 Richard Jones 和 Fred Drake 實現。

PEP 328: 絕對匯入和相對匯入

PEP 328 的簡單部分已在 Python 2.4 中實現:現在可以使用括號將使用 from ... import ... 語句從模組匯入的名稱括起來,從而更容易匯入許多不同的名稱。

更復雜的部分已在 Python 2.5 中實現:匯入模組可以指定使用絕對匯入或包相對匯入。計劃是在 Python 的未來版本中將絕對匯入設為預設。

假設你有一個像這樣的包目錄:

pkg/
pkg/__init__.py
pkg/main.py
pkg/string.py

這定義了一個名為 pkg 的包,其中包含 pkg.mainpkg.string 子模組。

考慮 main.py 模組中的程式碼。如果它執行語句 import string 會發生什麼?在 Python 2.4 及更早版本中,它會首先在包的目錄中查詢以執行相對匯入,找到 pkg/string.py,將該檔案的內容作為 pkg.string 模組匯入,並且該模組繫結到 pkg.main 模組名稱空間中的名稱 string

如果你想要的是 pkg.string,那就沒問題。但是如果你想要 Python 的標準 string 模組呢?沒有一種簡潔的方法可以忽略 pkg.string 並尋找標準模組;通常你必須檢視 sys.modules 的內容,這有點不乾淨。Holger Krekel 的 py.std 包提供了一種更整潔的方法來從標準庫執行匯入,import py; py.std.string.join(),但該包並非在所有 Python 安裝中都可用。

閱讀依賴相對匯入的程式碼也比較不清晰,因為讀者可能會對意圖使用哪個模組(stringpkg.string)感到困惑。Python 使用者很快就學會了不要在包的子模組名稱中重複標準庫模組的名稱,但你無法阻止你的子模組名稱在 Python 未來版本中被用於新模組。

在 Python 2.5 中,你可以使用 from __future__ import absolute_import 指令將 import 的行為切換為絕對匯入。這種絕對匯入行為將在未來的版本(可能是 Python 2.7)中成為預設。一旦絕對匯入成為預設,import string 將始終找到標準庫的版本。建議使用者應儘可能開始使用絕對匯入,因此最好在程式碼中開始編寫 from pkg import string

使用 from ... import 形式時,透過在模組名稱前新增一個句點,仍然可以進行相對匯入。

# Import names from pkg.string
from .string import name1, name2
# Import pkg.string
from . import string

這將匯入相對於當前包的 string 模組,因此在 pkg.main 中,這將從 pkg.string 匯入 *name1* 和 *name2*。額外的前導句點將從當前包的父包開始執行相對匯入。例如,A.B.C 模組中的程式碼可以執行:

from . import D                 # Imports A.B.D
from .. import E                # Imports A.E
from ..F import G               # Imports A.F.G

前導句點不能與 import modname 形式的 import 語句一起使用,只能與 from ... import 形式一起使用。

參見

PEP 328 - 匯入:多行與絕對/相對

PEP 由 Aahz 編寫;由 Thomas Wouters 實現。

https://pylib.readthedocs.io/

Holger Krekel 的 py 庫,其中包含 py.std 包。

PEP 338: 將模組作為指令碼執行

Python 2.4 中新增的 -m 開關用於將模組作為指令碼執行,它獲得了一些額外的功能。該開關不再在 Python 直譯器內部的 C 程式碼中實現,而是使用一個新模組 runpy 中的實現。

runpy 模組實現了一種更復雜的匯入機制,因此現在可以執行包中的模組,例如 pychecker.checker。該模組還支援替代的匯入機制,例如 zipimport 模組。這意味著你可以將 .zip 存檔的路徑新增到 sys.path,然後使用 -m 開關來執行存檔中的程式碼。

參見

PEP 338 - 將模組作為指令碼執行

由 Nick Coghlan 撰寫和實現。

PEP 341: 統一的 try/except/finally

在 Python 2.5 之前,try 語句有兩種形式。你可以使用 finally 塊來確保程式碼總是被執行,或者一個或多個 except 塊來捕獲特定異常。你不能同時使用 except 塊和 finally 塊,因為為組合版本生成正確的位元組碼很複雜,並且不清楚組合語句的語義應該是什麼。

Guido van Rossum 花了一些時間研究 Java,它確實支援結合 except 塊和 finally 塊的等效功能,這澄清了該語句的含義。在 Python 2.5 中,你現在可以這樣寫:

try:
    block-1 ...
except Exception1:
    handler-1 ...
except Exception2:
    handler-2 ...
else:
    else-block
finally:
    final-block

執行 *塊-1* 中的程式碼。如果程式碼引發異常,則會測試各種 except 塊:如果異常屬於 Exception1 類,則執行 *處理程式-1*;否則,如果它屬於 Exception2 類,則執行 *處理程式-2*,依此類推。如果沒有引發異常,則執行 *else-block*。

無論之前發生了什麼,一旦程式碼塊完成並且任何引發的異常得到處理,*final-block* 都會執行。即使在異常處理程式或 *else-block* 中出現錯誤並引發新異常,*final-block* 中的程式碼仍會執行。

參見

PEP 341 - 統一 try-except 和 try-finally

PEP 由 Georg Brandl 編寫;由 Thomas Lee 實現。

PEP 342: 新的生成器功能

Python 2.5 添加了一種簡單的方法來將值 *傳入* 生成器。正如 Python 2.3 中引入的,生成器只產生輸出;一旦呼叫生成器程式碼建立迭代器,在恢復執行時就沒有辦法將任何新資訊傳遞給函式。有時,能夠傳入一些資訊會很有用。解決此問題的取巧方法包括讓生成器程式碼檢視全域性變數,然後更改全域性變數的值,或傳入一些可變物件,然後呼叫者修改它。

為了喚起你對基本生成器的記憶,這裡有一個簡單的例子:

def counter (maximum):
    i = 0
    while i < maximum:
        yield i
        i += 1

當你呼叫 counter(10) 時,結果是一個迭代器,它返回從 0 到 9 的值。遇到 yield 語句時,迭代器返回提供的值並暫停函式的執行,同時保留區域性變數。執行在對迭代器的 next() 方法的後續呼叫上恢復,從 yield 語句之後繼續。

在 Python 2.3 中,yield 是一個語句;它不返回任何值。在 2.5 中,yield 現在是一個表示式,返回一個可以賦值給變數或進行其他操作的值:

val = (yield i)

我建議你在處理返回值時,總是將 yield 表示式用括號括起來,就像上面的例子一樣。括號並非總是必要的,但總是新增它們比記住何時需要它們更容易。

PEP 342 解釋了確切的規則,即 yield 表示式必須始終用括號括起來,除非它出現在賦值語句右側的頂層表示式中。這意味著你可以寫 val = yield i,但在有操作時必須使用括號,例如 val = (yield i) + 12。)

透過呼叫生成器的 send(value) 方法,可以將值傳送到生成器中。然後,生成器程式碼恢復執行,yield 表示式返回指定的 *value*。如果呼叫常規的 next() 方法,yield 返回 None

以下是上一個示例的修改版本,允許更改內部計數器的值。

def counter (maximum):
    i = 0
    while i < maximum:
        val = (yield i)
        # If value provided, change counter
        if val is not None:
            i = val
        else:
            i += 1

這是更改計數器的一個例子:

>>> it = counter(10)
>>> print it.next()
0
>>> print it.next()
1
>>> print it.send(8)
8
>>> print it.next()
9
>>> print it.next()
Traceback (most recent call last):
  File "t.py", line 15, in ?
    print it.next()
StopIteration

yield 通常會返回 None,所以你應該總是檢查這種情況。不要只在表示式中使用它的值,除非你確定 send() 方法將是用於恢復生成器函式的唯一方法。

除了 send(),生成器還有另外兩個新方法:

  • throw(type, value=None, traceback=None) 用於在生成器內部引發異常;異常由生成器暫停執行的 yield 表示式引發。

  • close() 在生成器內部引發一個新的 GeneratorExit 異常以終止迭代。收到此異常後,生成器的程式碼必須要麼引發 GeneratorExit,要麼引發 StopIteration。捕獲 GeneratorExit 異常並返回值是非法的,並將觸發 RuntimeError;如果函式引發其他異常,則該異常將傳播給呼叫者。close() 也將在生成器被垃圾回收時由 Python 的垃圾回收器呼叫。

    如果您需要在發生 GeneratorExit 時執行清理程式碼,我建議使用 try: ... finally: 語句,而不是捕獲 GeneratorExit

這些更改的累積效應是將生成器從單向資訊生產者轉變為生產者和消費者。

生成器也變成了 *協程*,一種更通用的子程式形式。子程式在一個點進入,在另一個點退出(函式的頂部和 return 語句),但協程可以在許多不同點進入、退出和恢復(yield 語句)。我們將不得不找出在 Python 中有效使用協程的模式。

close() 方法的新增有一個不明顯的副作用。close() 在生成器被垃圾回收時被呼叫,這意味著生成器程式碼在生成器被銷燬之前有最後一次執行的機會。這最後一次機會意味著生成器中的 try...finally 語句現在可以保證工作;finally 子句現在總有機會執行。因此,不能將 yield 語句與 try...finally 語句混合使用的語法限制已被刪除。這看起來像一個小的語言知識點,但使用生成器和 try...finally 實際上是實現 PEP 343 所描述的 with 語句所必需的。我將在下一節中檢視這個新語句。

這一變化還有一個更深奧的影響:以前,生成器的 gi_frame 屬性始終是一個幀物件。現在,一旦生成器耗盡,gi_frame 可能是 None

參見

PEP 342 - 透過增強型生成器實現協程

PEP 由 Guido van Rossum 和 Phillip J. Eby 撰寫;由 Phillip J. Eby 實現。包括一些更高階的將生成器用作協程的示例。

這些功能的早期版本在 Raymond Hettinger 的 PEP 288 和 Samuele Pedroni 的 PEP 325 中提出。

https://en.wikipedia.org/wiki/Coroutine

協程的維基百科條目。

https://web.archive.org/web/20160321211320/http://www.sidhe.org/~dan/blog/archives/000178.html

Dan Sugalski 從 Perl 的角度解釋協程。

PEP 343: 'with' 語句

with” 語句澄清了以前會使用 try...finally 塊來確保清理程式碼執行的程式碼。在本節中,我將討論該語句的常見用法。在下一節中,我將檢查實現細節並展示如何編寫用於該語句的物件。

with” 語句是一種新的控制流結構,其基本結構是:

with expression [as variable]:
    with-block

表示式被求值,它應該返回一個支援上下文管理協議的物件(即具有 __enter__()__exit__() 方法)。

在執行 *with-block* 之前會呼叫物件的 __enter__() 方法,因此它可以執行設定程式碼。如果指定了 *variable*,它還可以返回一個值並繫結到 *variable* 名稱。(請注意,*variable* 並非被賦值為 *expression* 的結果。)

執行完 *with-block* 後,即使該塊引發了異常,也會呼叫物件的 __exit__() 方法,因此它可以執行清理程式碼。

要在 Python 2.5 中啟用該語句,你需要將以下指令新增到你的模組中:

from __future__ import with_statement

該語句將在 Python 2.6 中始終啟用。

一些標準的 Python 物件現在支援上下文管理協議,並且可以與 ' with ' 語句一起使用。檔案物件就是其中一個例子:

with open('/etc/passwd', 'r') as f:
    for line in f:
        print line
        ... more processing code ...

該語句執行後,即使 for 迴圈在塊執行過程中引發了異常,*f* 中的檔案物件也會自動關閉。

備註

在這種情況下,*f* 與 open() 建立的物件相同,因為 __enter__() 返回 *self*。

threading 模組的鎖和條件變數也支援 ' with ' 語句:

lock = threading.Lock()
with lock:
    # Critical section of code
    ...

鎖在塊執行前獲取,並在塊完成後總是釋放。

decimal 模組中的新 localcontext() 函式使得儲存和恢復當前十進位制上下文變得容易,該上下文封裝了計算所需的精度和舍入特性。

from decimal import Decimal, Context, localcontext

# Displays with default precision of 28 digits
v = Decimal('578')
print v.sqrt()

with localcontext(Context(prec=16)):
    # All code in this block uses a precision of 16 digits.
    # The original context is restored on exiting the block.
    print v.sqrt()

編寫上下文管理器

在底層,'with' 語句相當複雜。大多數人只會將 'with' 與現有物件一起使用,不需要了解這些細節,所以如果你願意,可以跳過本節的其餘部分。新物件的作者將需要了解底層實現的細節,並應繼續閱讀。

上下文管理協議的高階解釋是:

  • 表示式被求值,結果應該是一個名為“上下文管理器”的物件。上下文管理器必須具有 __enter__()__exit__() 方法。

  • 呼叫上下文管理器的 __enter__() 方法。返回的值被賦值給 *VAR*。如果沒有 'as VAR' 子句,則該值被簡單丟棄。

  • 執行 *BLOCK* 中的程式碼。

  • 如果 *BLOCK* 引發異常,則會使用異常詳細資訊呼叫 __exit__(type, value, traceback),這些值與 sys.exc_info() 返回的值相同。該方法的返回值控制是否重新引發異常:任何假值都會重新引發異常,而 True 將導致抑制異常。你很少會想要抑制異常,因為如果你這樣做,包含 'with' 語句的程式碼作者將永遠不會意識到出了問題。

  • 如果 *BLOCK* 沒有引發異常,__exit__() 方法仍會被呼叫,但 *type*、*value* 和 *traceback* 都為 None

讓我們仔細考慮一個例子。我不會提供詳細的程式碼,而只會概述支援事務的資料庫所需的方法。

(對於不熟悉資料庫術語的人:一組對資料庫的更改被分組到一個事務中。事務可以提交,這意味著所有更改都寫入資料庫,或者回滾,這意味著所有更改都被丟棄,資料庫保持不變。有關更多資訊,請參閱任何資料庫教科書。)

我們假設有一個表示資料庫連線的物件。我們的目標是讓使用者編寫如下程式碼:

db_connection = DatabaseConnection()
with db_connection as cursor:
    cursor.execute('insert into ...')
    cursor.execute('delete from ...')
    # ... more operations ...

如果塊中的程式碼執行完美,事務應該提交;如果出現異常,事務應該回滾。這是我假設的 DatabaseConnection 的基本介面:

class DatabaseConnection:
    # Database interface
    def cursor (self):
        "Returns a cursor object and starts a new transaction"
    def commit (self):
        "Commits current transaction"
    def rollback (self):
        "Rolls back current transaction"

__enter__() 方法非常簡單,只需啟動一個新事務。對於此應用程式,生成的遊標物件將是一個有用的結果,因此該方法將返回它。使用者可以將其 ' with ' 語句新增到 as cursor 中,以將遊標繫結到變數名。

class DatabaseConnection:
    ...
    def __enter__ (self):
        # Code to start a new transaction
        cursor = self.cursor()
        return cursor

__exit__() 方法最複雜,因為大部分工作都必須在那裡完成。該方法必須檢查是否發生了異常。如果沒有異常,則提交事務。如果發生異常,則回滾事務。

在下面的程式碼中,執行將直接跳出函式,返回預設值 NoneNone 為假,因此異常將自動重新引發。如果您願意,可以更明確地在標記位置新增一個 return 語句。

class DatabaseConnection:
    ...
    def __exit__ (self, type, value, tb):
        if tb is None:
            # No exception, so commit
            self.commit()
        else:
            # Exception occurred, so rollback.
            self.rollback()
            # return False

contextlib 模組

新的 contextlib 模組提供了一些函式和一個裝飾器,它們對於編寫用於 ' with ' 語句的物件很有用。

該裝飾器名為 contextmanager(),它允許你編寫一個單獨的生成器函式,而不是定義一個新類。該生成器應該只生成一個值。yield 之前的程式碼將作為 __enter__() 方法執行,生成的值將是該方法的返回值,如果 ' with ' 語句的 as 子句存在,則該值將繫結到變數。 yield 之後的程式碼將在 __exit__() 方法中執行。塊中引發的任何異常都將由 yield 語句引發。

我們上一節的資料庫示例可以使用此裝飾器編寫為:

from contextlib import contextmanager

@contextmanager
def db_transaction (connection):
    cursor = connection.cursor()
    try:
        yield cursor
    except:
        connection.rollback()
        raise
    else:
        connection.commit()

db = DatabaseConnection()
with db_transaction(db) as cursor:
    ...

contextlib 模組還有一個 nested(mgr1, mgr2, ...) 函式,它結合了多個上下文管理器,這樣你就無需編寫巢狀的 'with' 語句。在此示例中,單個 'with' 語句既啟動資料庫事務又獲取執行緒鎖:

lock = threading.Lock()
with nested (db_transaction(db), lock) as (cursor, locked):
    ...

最後,closing(object) 函式返回 *object*,以便它可以繫結到一個變數,並在塊結束時呼叫 object.close

import urllib, sys
from contextlib import closing

with closing(urllib.urlopen('http://www.yahoo.com')) as f:
    for line in f:
        sys.stdout.write(line)

參見

PEP 343 - “with” 語句

PEP 由 Guido van Rossum 和 Nick Coghlan 撰寫;由 Mike Bland、Guido van Rossum 和 Neal Norwitz 實現。PEP 展示了為 'with' 語句生成的程式碼,這有助於瞭解該語句的工作原理。

contextlib 模組的文件。

PEP 352: 異常作為新式類

異常類現在可以是新式類,而不僅僅是經典類,內建的 Exception 類和所有標準內建異常(NameErrorValueError 等)現在都是新式類。

異常的繼承層次結構有所調整。在 2.5 中,繼承關係如下:

BaseException       # New in Python 2.5
|- KeyboardInterrupt
|- SystemExit
|- Exception
   |- (all other current built-in exceptions)

這種重新安排是因為人們經常希望捕獲所有指示程式錯誤的異常。KeyboardInterruptSystemExit 雖然不是錯誤,但通常表示明確的操作,例如使用者按下 Control-C 或程式碼呼叫 sys.exit()。一個裸的 except: 會捕獲所有異常,因此通常你需要列出 KeyboardInterruptSystemExit 以便重新引發它們。通常的模式是:

try:
    ...
except (KeyboardInterrupt, SystemExit):
    raise
except:
    # Log error...
    # Continue running program...

在 Python 2.5 中,你現在可以寫 except Exception 來達到相同的效果,捕獲通常指示錯誤的所有異常,但保留 KeyboardInterruptSystemExit 不動。與以前的版本一樣,一個裸的 except: 仍然捕獲所有異常。

在 Python 2.5 中,將字串作為異常引發,例如語句 raise "Error occurred",已被棄用,並將觸發警告。目標是在未來幾個版本中刪除字串異常功能。Python 3.0 的目標是要求任何作為異常引發的類都必須派生自 BaseException 或其子孫類,Python 2.x 系列的未來版本可能會開始強制執行此約束。因此,我建議您現在就開始讓所有異常類都派生自 Exception。有人建議在 Python 3.0 中刪除裸的 except: 形式,但 Guido van Rossum 尚未決定是否這樣做。

在 Python 2.5 中,將字串作為異常引發,例如語句 raise "Error occurred",已被棄用,並將觸發警告。目標是在幾個版本後移除字串異常功能。

參見

PEP 352 - 異常的必需超類

PEP 由 Brett Cannon 和 Guido van Rossum 編寫;由 Brett Cannon 實現。

PEP 353: 使用 ssize_t 作為索引型別

對 Python C API 的廣泛更改,使用新的 Py_ssize_t 型別定義而不是 int,這將允許直譯器在 64 位平臺上處理更多資料。此更改不影響 Python 在 32 位平臺上的容量。

Python 直譯器的各個部分使用 C 語言的 int 型別來儲存大小或計數;例如,列表或元組中的專案數儲存在 int 中。大多數 64 位平臺的 C 編譯器仍然將 int 定義為 32 位型別,這意味著列表只能包含最多 2**31 - 1 = 2147483647 個專案。(實際上有幾種不同的程式設計模型可供 64 位 C 編譯器使用——請參閱 https://unix.org/version2/whatsnew/lp64_wp.html 上的討論——但最常用的模型將 int 保留為 32 位。)

在 32 位平臺上,2147483647 個專案的限制並不重要,因為在達到長度限制之前,你就會耗盡記憶體。每個列表項都需要一個指標的空間,即 4 位元組,再加上表示該項的 PyObject 的空間。2147483647*4 位元組已經超出了 32 位地址空間所能包含的位元組數。

然而,在 64 位平臺上可以定址那麼多的記憶體。這種大小的列表的指標只需要 16 GiB 的空間,因此 Python 程式設計師構建如此大的列表並非不合理。因此,Python 直譯器必須更改為使用 int 以外的型別,這在 64 位平臺上將是 64 位型別。此更改將導致 64 位機器上的不相容性,因此認為現在進行轉換是值得的,因為 64 位使用者數量仍然相對較少。(在 5 或 10 年後,我們可能 *所有* 人都在使用 64 位機器,那時轉換會更痛苦。)

此更改對 C 擴充套件模組的作者影響最大。Python 字串和列表、元組等容器型別現在使用 Py_ssize_t 來儲存其大小。因此,像 PyList_Size() 這樣的函式現在返回 Py_ssize_t。擴充套件模組中的程式碼可能因此需要將一些變數更改為 Py_ssize_t

PyArg_ParseTuple()Py_BuildValue() 函式有一個新的轉換程式碼 n,用於 Py_ssize_tPyArg_ParseTuple()s#t# 預設仍然輸出 int,但在包含 Python.h 之前可以定義宏 PY_SSIZE_T_CLEAN 以使它們返回 Py_ssize_t

PEP 353 有一個關於轉換指南的部分,擴充套件作者應該閱讀該部分以瞭解如何支援 64 位平臺。

參見

PEP 353 - 使用 ssize_t 作為索引型別

PEP 由 Martin von Löwis 編寫並實現。

PEP 357: '__index__' 方法

NumPy 開發者遇到了一個只能透過新增一個新的特殊方法 __index__() 來解決的問題。當使用切片表示法時,例如 [start:stop:step],*start*、*stop* 和 *step* 索引的值都必須是整數或長整數。NumPy 定義了各種專門的整數型別,對應於 8、16、32 和 64 位的無符號和有符號整數,但沒有辦法表明這些型別可以用作切片索引。

切片不能直接使用現有的 __int__() 方法,因為該方法也用於實現強制轉換為整數。如果切片使用 __int__(),浮點數也將成為合法的切片索引,這顯然是不可取的行為。

相反,添加了一個新的特殊方法 __index__()。它不接受任何引數,並返回一個整數,表示要使用的切片索引。例如:

class C:
    def __index__ (self):
        return self.value

返回值必須是 Python 整數或長整數。直譯器將檢查返回型別是否正確,如果未滿足此要求,則會引發 TypeError

C 級的 PyNumberMethods 結構中添加了相應的 nb_index 槽,以便 C 擴充套件可以實現此協議。PyNumber_Index(obj) 可以在擴充套件程式碼中用於呼叫 __index__() 函式並檢索其結果。

參見

PEP 357 - 允許任意物件用於切片

PEP 由 Travis Oliphant 編寫並實現。

其他語言更改

以下是 Python 2.5 對核心 Python 語言進行的所有更改。

  • dict 型別有一個新的鉤子,允許子類在字典中不包含鍵時提供預設值。當未找到鍵時,將呼叫字典的 __missing__(key) 方法。此鉤子用於在 collections 模組中實現新的 defaultdict 類。以下示例定義了一個字典,該字典為任何缺失的鍵返回零

    class zerodict (dict):
        def __missing__ (self, key):
            return 0
    
    d = zerodict({1:1, 2:2})
    print d[1], d[2]   # Prints 1, 2
    print d[3], d[4]   # Prints 0, 0
    
  • 8 位字串和 Unicode 字串都有新的 partition(sep)rpartition(sep) 方法,這些方法簡化了一個常見的用例。

    find(S) 方法通常用於獲取索引,然後使用該索引對字串進行切片並獲取分隔符之前和之後的部分。partition(sep) 將此模式濃縮為一個方法呼叫,該呼叫返回一個 3 元組,其中包含分隔符之前的子字串、分隔符本身以及分隔符之後的子字串。如果未找到分隔符,則元組的第一個元素是整個字串,其他兩個元素為空。rpartition(sep) 也返回一個 3 元組,但從字串的末尾開始搜尋;r 代表“reverse”(反向)。

    一些例子

    >>> ('https://python.club.tw').partition('://')
    ('http', '://', 'www.python.org')
    >>> ('file:/usr/share/doc/index.html').partition('://')
    ('file:/usr/share/doc/index.html', '', '')
    >>> (u'Subject: a quick question').partition(':')
    (u'Subject', u':', u' a quick question')
    >>> 'www.python.org'.rpartition('.')
    ('www.python', '.', 'org')
    >>> 'www.python.org'.rpartition(':')
    ('', '', 'www.python.org')
    

    (由 Fredrik Lundh 根據 Raymond Hettinger 的建議實現。)

  • 字串型別的 startswith()endswith() 方法現在接受字串元組進行檢查。

    def is_image_file (filename):
        return filename.endswith(('.gif', '.jpg', '.tiff'))
    

    (由 Georg Brandl 根據 Tom Lynn 的建議實現。)

  • min()max() 內建函式獲得了類似於 sort()key 關鍵字引數。此引數提供一個函式,該函式接受單個引數併為列表中的每個值呼叫;min()/max() 將返回此函式返回值最小/最大的元素。例如,要查詢列表中最長的字串,您可以這樣做

    L = ['medium', 'longest', 'short']
    # Prints 'longest'
    print max(L, key=len)
    # Prints 'short', because lexicographically 'short' has the largest value
    print max(L)
    

    (由 Steven Bethard 和 Raymond Hettinger 貢獻。)

  • 兩個新的內建函式,any()all(),評估迭代器是否包含任何真值或假值。any() 如果迭代器返回的任何值為真,則返回 True>;否則返回 Falseall() 僅當迭代器返回的所有值都評估為真時才返回 True。(由 Guido van Rossum 建議,由 Raymond Hettinger 實現。)

  • 類的 __hash__() 方法的結果現在可以是長整數或常規整數。如果返回長整數,則取該值的雜湊值。在早期版本中,雜湊值必須是常規整數,但在 2.5 中,id() 內建函式已更改為始終返回非負數,並且使用者似乎經常在 __hash__() 方法中使用 id(self)(儘管不鼓勵這樣做)。

  • ASCII 現在是模組的預設編碼。如果模組包含帶有 8 位字元的字串文字但沒有編碼宣告,則現在是語法錯誤。在 Python 2.4 中,這會觸發警告,而不是語法錯誤。有關如何宣告模組編碼的資訊,請參閱 PEP 263;例如,您可以在原始檔頂部附近新增一行程式碼,如下所示

    # -*- coding: latin1 -*-
    
  • 當您嘗試比較 Unicode 字串和無法使用預設 ASCII 編碼轉換為 Unicode 的 8 位字串時,將觸發新的警告 UnicodeWarning。比較結果為 false

    >>> chr(128) == unichr(128)   # Can't convert chr(128) to Unicode
    __main__:1: UnicodeWarning: Unicode equal comparison failed
      to convert both arguments to Unicode - interpreting them
      as being unequal
    False
    >>> chr(127) == unichr(127)   # chr(127) can be converted
    True
    

    以前,這會引發 UnicodeDecodeError 異常,但在 2.5 中,這可能導致訪問字典時出現令人費解的問題。如果您查詢 unichr(128) 並且 chr(128) 被用作鍵,您將收到 UnicodeDecodeError 異常。2.5 中的其他更改導致此異常被引發,而不是被實現字典的 dictobject.c 中的程式碼抑制。

    對這樣的比較引發異常是嚴格正確的,但此更改可能會破壞程式碼,因此引入了 UnicodeWarning

    (由 Marc-André Lemburg 實現。)

  • Python 程式設計師有時會犯的一個錯誤是忘記在包目錄中包含 __init__.py 模組。除錯此錯誤可能會令人困惑,通常需要使用 -v 開關執行 Python 以記錄所有搜尋過的路徑。在 Python 2.5 中,當匯入會將目錄作為包拾取但未找到 __init__.py 時,會觸發新的 ImportWarning 警告。此警告預設情況下被靜默忽略;在執行 Python 可執行檔案時提供 -Wd 選項以顯示警告訊息。(由 Thomas Wouters 實現。)

  • 類定義中的基類列表現在可以為空。例如,現在以下是合法的

    class C():
        pass
    

    (由 Brett Cannon 實現。)

互動式直譯器更改

在互動式直譯器中,quitexit 長期以來都是字串,以便新使用者在嘗試退出時獲得一些有用的訊息

>>> quit
'Use Ctrl-D (i.e. EOF) to exit.'

在 Python 2.5 中,quitexit 現在是物件,它們仍然生成自身的字串表示,但也可以呼叫。嘗試 quit()exit() 的新手現在將按預期退出直譯器。(由 Georg Brandl 實現。)

Python 可執行檔案現在接受標準長選項 --help--version;在 Windows 上,它還接受 /? 選項以顯示幫助訊息。(由 Georg Brandl 實現。)

最佳化

其中一些最佳化是在冰島雷克雅未克於 2006 年 5 月 21 日至 28 日舉行的 NeedForSpeed 衝刺活動中開發的。該衝刺活動主要關注 CPython 實現的速度提升,由 EWT LLC 資助,並得到 CCP Games 的當地支援。在此衝刺活動中新增的最佳化在以下列表中特別標註。

  • 當在 Python 2.4 中引入時,內建的 setfrozenset 型別是建立在 Python 字典型別之上的。在 2.5 中,內部資料結構已為實現集合進行了定製,結果是集合將減少三分之一的記憶體使用量並稍快一些。(由 Raymond Hettinger 實現。)

  • 一些 Unicode 操作的速度得到了提高,例如查詢子字串、字串分割以及字元對映編碼和解碼。(子字串搜尋和分割的改進由 Fredrik Lundh 和 Andrew Dalke 在 NeedForSpeed 衝刺活動中新增。字元對映由 Walter Dörwald 和 Martin von Löwis 改進。)

  • long(str, base) 函式現在在長數字字串上速度更快,因為計算的中間結果更少。峰值出現在大約 800-1000 位數字的字串上,函式速度提高了 6 倍。(由 Alan McIntyre 貢獻並在 NeedForSpeed 衝刺活動中提交。)

  • 現在,混合使用 for line in file 迭代檔案和呼叫檔案物件的 read()/readline()/readlines() 方法是違法的。迭代使用內部緩衝區,而 read*() 方法不使用該緩衝區。相反,它們將返回緩衝區後面的資料,導致資料出現亂序。混合使用迭代和這些方法現在將從 read*() 方法觸發 ValueError。(由 Thomas Wouters 實現。)

  • struct 模組現在將結構格式字串編譯成內部表示並快取此表示,從而提高了 20% 的速度。(由 Bob Ippolito 在 NeedForSpeed 衝刺活動中貢獻。)

  • re 模組透過切換到 Python 的分配器函式而不是系統的 malloc()free(),獲得了 1% 或 2% 的速度提升。(由 Jack Diederich 在 NeedForSpeed 衝刺活動中貢獻。)

  • 程式碼生成器的窺孔最佳化器現在執行表示式中的簡單常量摺疊。如果您編寫 a = 2+3 之類的東西,程式碼生成器將執行算術運算並生成與 a = 5 對應的程式碼。(由 Raymond Hettinger 提出並實現。)

  • 函式呼叫現在更快,因為程式碼物件現在將最近完成的幀(“殭屍幀”)儲存在程式碼物件的內部欄位中,並在下次呼叫程式碼物件時重用它。(Michael Hudson 的原始補丁,由 Armin Rigo 和 Richard Jones 修改;在 NeedForSpeed 衝刺活動中提交。)幀物件也略小,這可能會提高快取區域性性並略微減少記憶體使用。(由 Neal Norwitz 貢獻。)

  • Python 的內建異常現在是新式類,這一更改大大加快了例項化速度。因此,Python 2.5 中的異常處理比 2.4 快約 30%。(由 Richard Jones、Georg Brandl 和 Sean Reifschneider 在 NeedForSpeed 衝刺活動中貢獻。)

  • 匯入現在會快取嘗試過的路徑,記錄它們是否存在,以便直譯器在啟動時進行更少的 open()stat() 呼叫。(由 Martin von Löwis 和 Georg Brandl 貢獻。)

新增、改進和刪除的模組

Python 2.5 中,標準庫得到了許多增強和錯誤修復。以下是最顯著更改的部分列表,按模組名稱字母順序排序。有關更完整的更改列表,請查閱原始碼樹中的 Misc/NEWS 檔案,或檢視 SVN 日誌以獲取所有詳細資訊。

  • audioop 模組現在支援 a-LAW 編碼,並且 u-LAW 編碼的程式碼已得到改進。(由 Lars Immisch 貢獻。)

  • codecs 模組增加了對增量編解碼器的支援。codec.lookup() 函式現在返回一個 CodecInfo 例項而不是一個元組。CodecInfo 例項表現為 4 元組以保持向後相容性,但也具有 encodedecodeincrementalencoderincrementaldecoderstreamwriterstreamreader 屬性。增量編解碼器可以接收輸入並以多個塊生成輸出;輸出與將整個輸入提供給非增量編解碼器相同。有關詳細資訊,請參閱 codecs 模組文件。(由 Walter Dörwald 設計和實現。)

  • collections 模組增加了一個新型別 defaultdict,它是標準 dict 型別的子類。新型別大部分行為像字典,但在鍵不存在時構造一個預設值,並自動將其新增到字典中以獲取請求的鍵值。

    defaultdict 建構函式的第一個引數是一個工廠函式,當請求的鍵未找到時,會呼叫該函式。此工廠函式不接收任何引數,因此您可以使用內建型別建構函式,例如 list()int()。例如,您可以像這樣根據單詞的首字母建立索引

    words = """Nel mezzo del cammin di nostra vita
    mi ritrovai per una selva oscura
    che la diritta via era smarrita""".lower().split()
    
    index = defaultdict(list)
    
    for w in words:
        init_letter = w[0]
        index[init_letter].append(w)
    

    列印 index 會得到以下輸出

    defaultdict(<type 'list'>, {'c': ['cammin', 'che'], 'e': ['era'],
            'd': ['del', 'di', 'diritta'], 'm': ['mezzo', 'mi'],
            'l': ['la'], 'o': ['oscura'], 'n': ['nel', 'nostra'],
            'p': ['per'], 's': ['selva', 'smarrita'],
            'r': ['ritrovai'], 'u': ['una'], 'v': ['vita', 'via']}
    

    (由 Guido van Rossum 貢獻。)

  • collections 模組提供的 deque 雙端佇列型別現在有一個 remove(value) 方法,該方法從佇列中刪除 value 的第一個出現,如果未找到該值則引發 ValueError。(由 Raymond Hettinger 貢獻。)

  • 新模組:contextlib 模組包含與新的“with”語句一起使用的輔助函式。有關此模組的更多資訊,請參閱 contextlib 模組 部分。

  • 新模組:cProfile 模組是現有 profile 模組的 C 實現,開銷大大降低。該模組的介面與 profile 相同:您執行 cProfile.run('main()') 來分析函式,可以將分析資料儲存到檔案等。目前尚不清楚 Hotshot 分析器(它也是用 C 編寫的,但不匹配 profile 模組的介面)是否會在未來版本的 Python 中繼續維護。(由 Armin Rigo 貢獻。)

    此外,用於分析器測量的資料的 pstats 模組現在支援透過向 Stats 建構函式提供 stream 引數來將輸出定向到任何檔案物件。(由 Skip Montanaro 貢獻。)

  • csv 模組,用於解析逗號分隔值格式的檔案,獲得了多項增強和許多錯誤修復。您現在可以透過呼叫 csv.field_size_limit(new_limit) 函式來設定欄位的最大位元組大小;省略 new_limit 引數將返回當前設定的限制。reader 類現在有一個 line_num 屬性,該屬性計算從源讀取的物理行數;記錄可以跨多個物理行,因此 line_num 與讀取的記錄數不同。

    CSV 解析器現在對多行帶引號的欄位更加嚴格。以前,如果一行在帶引號的欄位中結束而沒有終止換行符,則會在返回的欄位中插入一個換行符。這種行為在讀取包含欄位內回車符的檔案時導致問題,因此程式碼已更改為返回不插入換行符的欄位。因此,如果欄位中嵌入的換行符很重要,則應以保留換行符的方式將輸入拆分為行。

    (由 Skip Montanaro 和 Andrew McNamara 貢獻。)

  • datetime 模組中的 datetime 類現在有一個 strptime(string, format) 方法用於解析日期字串,由 Josh Spoerri 貢獻。它使用與 time.strptime()time.strftime() 相同的格式字元

    from datetime import datetime
    
    ts = datetime.strptime('10:13:15 2006-03-07',
                           '%H:%M:%S %Y-%m-%d')
    
  • difflib 模組中的 SequenceMatcher.get_matching_blocks() 方法現在保證返回描述匹配子序列的最少塊列表。以前,演算法偶爾會將匹配元素塊分成兩個列表條目。(由 Tim Peters 增強。)

  • doctest 模組增加了一個 SKIP 選項,可以完全阻止示例的執行。這適用於作為用法示例而非實際測試用例的程式碼片段。

    testfile() 函式和 DocFileSuite 類中添加了一個 encoding 引數,用於指定檔案的編碼。這使得在文件字串中包含非 ASCII 字元的測試更容易使用。(由 Bjorn Tillenius 貢獻。)

  • email 包已更新到版本 4.0。(由 Barry Warsaw 貢獻。)

  • fileinput 模組變得更加靈活。現在支援 Unicode 檔名,並且在 input() 函式中添加了一個預設為 "r"mode 引數,以允許以二進位制或 通用換行符 模式開啟檔案。另一個新引數 openhook 允許您使用 open() 以外的函式開啟輸入檔案。一旦您迭代檔案集,FileInput 物件的 fileno() 返回當前開啟檔案的檔案描述符。(由 Georg Brandl 貢獻。)

  • gc 模組中,新的 get_count() 函式返回一個 3 元組,其中包含三個 GC 代的當前收集計數。這是垃圾收集器的記賬資訊;當這些計數達到指定閾值時,將進行垃圾收集掃描。現有的 gc.collect() 函式現在接受一個可選的 0、1 或 2 的 generation 引數,以指定要收集哪一代。(由 Barry Warsaw 貢獻。)

  • heapq 模組中的 nsmallest()nlargest() 函式現在支援 key 關鍵字引數,類似於 min()/max() 函式和 sort() 方法提供的引數。例如

    >>> import heapq
    >>> L = ["short", 'medium', 'longest', 'longer still']
    >>> heapq.nsmallest(2, L)  # Return two lowest elements, lexicographically
    ['longer still', 'longest']
    >>> heapq.nsmallest(2, L, key=len)   # Return two shortest elements
    ['short', 'medium']
    

    (由 Raymond Hettinger 貢獻。)

  • itertools.islice() 函式現在接受 None 作為 start 和 step 引數。這使得它與切片物件的屬性更加相容,因此您現在可以編寫以下內容

    s = slice(5)     # Create slice object
    itertools.islice(iterable, s.start, s.stop, s.step)
    

    (由 Raymond Hettinger 貢獻。)

  • locale 模組中的 format() 函式已修改,並添加了兩個新函式:format_string()currency()

    以前,format() 函式的 val 引數可以是字串,只要不超過一個 %char 說明符出現;現在該引數必須正好是一個 %char 說明符,且沒有周圍的文字。還添加了一個可選的 monetary 引數,如果為 True,則將使用語言環境的規則來格式化貨幣,在三位數字組之間放置分隔符。

    要格式化帶有多個 %char 說明符的字串,請使用新的 format_string() 函式,該函式的工作方式與 format() 相同,但也支援將 %char 說明符與任意文字混合。

    還添加了一個新的 currency() 函式,該函式根據當前語言環境的設定格式化數字。

    (由 Georg Brandl 貢獻。)

  • mailbox 模組經過了大規模重寫,除了讀取郵箱之外,還增加了修改郵箱的功能。一套新的類,包括 mboxMHMaildir,用於讀取郵箱,並具有 add(message) 方法用於新增訊息,remove(key) 用於刪除訊息,以及 lock()/unlock() 用於鎖定/解鎖郵箱。以下示例將 maildir 格式的郵箱轉換為 mbox 格式

    import mailbox
    
    # 'factory=None' uses email.Message.Message as the class representing
    # individual messages.
    src = mailbox.Maildir('maildir', factory=None)
    dest = mailbox.mbox('/tmp/mbox')
    
    for msg in src:
        dest.add(msg)
    

    (由 Gregory K. Johnson 貢獻。資金由 Google 的 2005 年 Summer of Code 提供。)

  • 新模組:msilib 模組允許建立 Microsoft Installer .msi 檔案和 CAB 檔案。還包含一些對讀取 .msi 資料庫的支援。(由 Martin von Löwis 貢獻。)

  • nis 模組現在支援透過向 nis.match()nis.maps() 函式提供 domain 引數來訪問系統預設域以外的域。(由 Ben Bell 貢獻。)

  • operator 模組的 itemgetter()attrgetter() 函式現在支援多個欄位。呼叫 operator.attrgetter('a', 'b') 將返回一個檢索 ab 屬性的函式。將此新功能與 sort() 方法的 key 引數結合使用,可以輕鬆地使用多個欄位對列表進行排序。(由 Raymond Hettinger 貢獻。)

  • optparse 模組已更新至 Optik 庫的 1.5.1 版本。OptionParser 類獲得了一個 epilog 屬性(一個將在幫助訊息後列印的字串)和一個 destroy() 方法來打破物件建立的引用迴圈。(由 Greg Ward 貢獻。)

  • os 模組經過了幾項更改。stat_float_times 變數現在預設為 true,這意味著 os.stat() 現在將時間值作為浮點數返回。(這不一定意味著 os.stat() 將返回精確到秒分秒的時間;並非所有系統都支援這種精度。)

    已新增名為 os.SEEK_SETos.SEEK_CURos.SEEK_END 的常量;這些是 os.lseek() 函式的引數。兩個新的鎖定常量是 os.O_SHLOCKos.O_EXLOCK

    添加了兩個新函式:wait3()wait4()。它們與 waitpid() 函式類似,後者等待子程序退出並返回程序 ID 及其退出狀態的元組,但 wait3()wait4() 返回附加資訊。wait3() 不接受程序 ID 作為輸入,因此它等待任何子程序退出並返回 程序 ID退出狀態資源使用情況 的 3 元組,其值由 resource.getrusage() 函式返回。wait4(pid) 確實接受程序 ID。(由 Chad J. Schroeder 貢獻。)

    在 FreeBSD 上,os.stat() 函式現在返回納秒級精度的時間,並且返回的物件現在具有 st_genst_birthtime 屬性。如果平臺支援,st_flags 屬性也可用。(由 Antti Louko 和 Diego Pettenò 貢獻。)

  • pdb 模組提供的 Python 偵錯程式現在可以儲存命令列表,以便在達到斷點並停止執行時執行。建立斷點 #1 後,輸入 commands 1 並輸入一系列要執行的命令,以 end 結束列表。命令列表可以包含恢復執行的命令,例如 continuenext。(由 Grégoire Dooms 貢獻。)

  • picklecPickle 模組不再接受 __reduce__() 方法返回 None;該方法必須返回一個引數元組。返回 None 的能力在 Python 2.4 中已棄用,因此這完成了該功能的刪除。

  • pkgutil 模組,包含用於查詢包的各種實用函式,已增強以支援 PEP 302 的匯入鉤子,現在也適用於儲存在 ZIP 格式存檔中的包。(由 Phillip J. Eby 貢獻。)

  • Marc-André Lemburg 的 pybench 基準測試套件現在包含在 Tools/pybench 目錄中。pybench 套件是對常用 pystone.py 程式的改進,因為 pybench 提供了更詳細的直譯器速度測量。它測量特定的操作,例如函式呼叫、元組切片、方法查詢和數值操作,而不是像 pystone.py 那樣執行許多不同的操作並將結果簡化為單個數字。

  • pyexpat 模組現在使用 Expat 解析器的 2.0 版本。(由 Trent Mick 貢獻。)

  • Queue 模組提供的 Queue 類獲得了兩個新方法。join() 阻塞直到佇列中的所有項都被檢索並且所有項上的處理工作都已完成。工作執行緒呼叫另一個新方法 task_done(),以指示某個項的處理已完成。(由 Raymond Hettinger 貢獻。)

  • 舊的 regexregsub 模組(自 Python 2.0 以來已棄用)最終已被刪除。其他已刪除的模組:statcachetzparsewhrandom

  • 同樣刪除的還有 lib-old 目錄,其中包含一些舊模組,例如 dircmpnilib-old 不在預設的 sys.path 中,因此除非您的程式明確將該目錄新增到 sys.path,否則此刪除應該不會影響您的程式碼。

  • rlcompleter 模組不再依賴於匯入 readline 模組,因此現在可以在非 Unix 平臺上工作。(來自 Robert Kiendl 的補丁。)

  • SimpleXMLRPCServerDocXMLRPCServer 類現在具有一個 rpc_paths 屬性,該屬性將 XML-RPC 操作限制在有限的 URL 路徑集內;預設只允許 '/''/RPC2'。將 rpc_paths 設定為 None 或空元組會停用此路徑檢查。

  • socket 模組現在支援 Linux 上的 AF_NETLINK 套接字,這要歸功於 Philippe Biondi 的補丁。Netlink 套接字是 Linux 特有的使用者空間程序與核心程式碼之間通訊的機制;有關它們的介紹性文章位於 https://www.linuxjournal.com/article/7356。在 Python 程式碼中,netlink 地址表示為 2 個整數的元組,(pid, group_mask)

    套接字物件上的兩個新方法 recv_into(buffer)recvfrom_into(buffer) 將接收到的資料儲存在支援緩衝區協議的物件中,而不是將資料作為字串返回。這意味著您可以將資料直接放入陣列或記憶體對映檔案中。

    套接字物件還獲得了 getfamily()gettype()getproto() 訪問器方法,以檢索套接字的族、型別和協議值。

  • 新模組:spwd 模組提供用於在支援影子密碼的系統上訪問影子密碼資料庫的函式。

  • struct 現在更快,因為它將格式字串編譯成帶有 pack()unpack() 方法的 Struct 物件。這類似於 re 模組允許您建立已編譯的正則表示式物件的方式。您仍然可以使用模組級別的 pack()unpack() 函式;它們將建立 Struct 物件並快取它們。或者您可以直接使用 Struct 例項

    s = struct.Struct('ih3s')
    
    data = s.pack(1972, 187, 'abc')
    year, number, name = s.unpack(data)
    

    您還可以使用 pack_into(buffer, offset, v1, v2, ...)unpack_from(buffer, offset) 方法直接將資料打包和解包到緩衝區物件。這使您可以將資料直接儲存到陣列或記憶體對映檔案中。

    (Struct 物件由 Bob Ippolito 在 NeedForSpeed sprint 中實現。對緩衝區物件的支援由 Martin Blais 新增,同樣在 NeedForSpeed sprint 中。)

  • Python 開發者在 2.5 開發過程中從 CVS 切換到 Subversion。關於確切構建版本的資訊以 sys.subversion 變數的形式提供,它是一個 3 元組 (interpreter-name, branch-name, revision-range)。例如,在編寫本文時,我的 2.5 版本報告為 ('CPython', 'trunk', '45313:45315')

    此資訊也透過 Py_GetBuildInfo() 函式提供給 C 擴充套件,該函式返回一個構建資訊字串,如下所示:"trunk:45355:45356M, Apr 13 2006, 07:42:19"。(由 Barry Warsaw 貢獻。)

  • 另一個新函式 sys._current_frames() 返回所有正在執行執行緒的當前堆疊幀,作為一個字典,將執行緒識別符號對映到呼叫函式時在該執行緒中當前活動的最高層堆疊幀。(由 Tim Peters 貢獻。)

  • tarfile 模組中的 TarFile 類現在有一個 extractall() 方法,該方法將存檔中的所有成員提取到當前工作目錄中。也可以將不同的目錄設定為提取目標,並且只解包存檔成員的子集。

    現在可以使用模式 'r|*' 自動檢測以流模式開啟的 tarfile 所使用的壓縮。 (由 Lars Gustäbel 貢獻。)

  • threading 模組現在允許您設定建立新執行緒時使用的堆疊大小。stack_size([*size*]) 函式返回當前配置的堆疊大小,提供可選的 size 引數將設定一個新值。並非所有平臺都支援更改堆疊大小,但 Windows、POSIX 執行緒和 OS/2 都支援。(由 Andrew MacIntyre 貢獻。)

  • unicodedata 模組已更新為使用 Unicode 字元資料庫的 4.1.0 版本。版本 3.2.0 是某些規範所要求的,因此它仍然作為 unicodedata.ucd_3_2_0 提供。

  • 新模組:uuid 模組根據 RFC 4122 生成通用唯一識別符號 (UUID)。RFC 定義了幾種不同的 UUID 版本,它們由起始字串、系統屬性或純隨機生成。此模組包含一個 UUID 類和名為 uuid1()uuid3()uuid4()uuid5() 的函式,用於生成不同版本的 UUID。(RFC 4122 未指定版本 2 UUID,此模組不支援。)

    >>> import uuid
    >>> # make a UUID based on the host ID and current time
    >>> uuid.uuid1()
    UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
    
    >>> # make a UUID using an MD5 hash of a namespace UUID and a name
    >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
    UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
    
    >>> # make a random UUID
    >>> uuid.uuid4()
    UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
    
    >>> # make a UUID using a SHA-1 hash of a namespace UUID and a name
    >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
    UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
    

    (由 Ka-Ping Yee 貢獻。)

  • weakref 模組的 WeakKeyDictionaryWeakValueDictionary 型別獲得了用於迭代字典中包含的弱引用的新方法。iterkeyrefs()keyrefs() 方法已新增到 WeakKeyDictionaryitervaluerefs()valuerefs() 已新增到 WeakValueDictionary。(由 Fred L. Drake, Jr. 貢獻。)

  • webbrowser 模組獲得多項增強。現在可以作為指令碼使用 python -m webbrowser,並以 URL 作為引數;有許多開關可以控制行為(-n 用於新瀏覽器視窗,-t 用於新標籤頁)。添加了新的模組級函式 open_new()open_new_tab() 來支援此功能。該模組的 open() 函式支援一項附加功能,即 autoraise 引數,該引數指示是否儘可能提升開啟的視窗。支援列表中添加了許多其他瀏覽器,例如 Firefox、Opera、Konqueror 和 elinks。(由 Oleg Broytmann 和 Georg Brandl 貢獻。)

  • xmlrpclib 模組現在支援為 XML-RPC 日期型別返回 datetime 物件。向 loads() 函式或 Unmarshaller 類提供 use_datetime=True 以啟用此功能。(由 Skip Montanaro 貢獻。)

  • zipfile 模組現在支援 ZIP64 版本的格式,這意味著 .zip 存檔現在可以大於 4 GiB,並且可以包含大於 4 GiB 的單個檔案。(由 Ronald Oussoren 貢獻。)

  • zlib 模組的 CompressDecompress 物件現在支援 copy() 方法,該方法複製物件的內部狀態並返回一個新的 CompressDecompress 物件。(由 Chris AtLee 貢獻。)

ctypes 包

由 Thomas Heller 編寫的 ctypes 包已新增到標準庫中。ctypes 允許您呼叫共享庫或 DLL 中的任意函式。長期使用者可能還記得 dl 模組,它提供了載入共享庫和呼叫其中函式的函式。ctypes 包更加高階。

要載入共享庫或 DLL,您必須建立 CDLL 類的例項,並提供共享庫或 DLL 的名稱或路徑。完成後,您可以透過訪問 CDLL 物件的屬性來呼叫任意函式。

import ctypes

libc = ctypes.CDLL('libc.so.6')
result = libc.printf("Line of output\n")

提供了各種 C 型別的型別建構函式:c_int()c_float()c_double()c_char_p()(等同於 char*)等等。與 Python 的型別不同,C 版本都是可變的;您可以分配給它們的 value 屬性以更改包裝的值。Python 整數和字串將自動轉換為相應的 C 型別,但對於其他型別,您必須呼叫正確的型別建構函式。(而且我指的是 *必須*;弄錯通常會導致直譯器崩潰,出現段錯誤。)

當 C 函式會修改記憶體區域時,不應將 c_char_p() 與 Python 字串一起使用,因為 Python 字串應該是不可變的;違反此規則會導致令人費解的錯誤。當您需要可修改的記憶體區域時,請使用 create_string_buffer()

s = "this is a string"
buf = ctypes.create_string_buffer(s)
libc.strfry(buf)

C 函式假定返回整數,但您可以設定函式物件的 restype 屬性來更改此設定

>>> libc.atof('2.71828')
-1783957616
>>> libc.atof.restype = ctypes.c_double
>>> libc.atof('2.71828')
2.71828

ctypes 還透過 ctypes.pythonapi 物件為 Python 的 C API 提供了包裝器。此物件在呼叫函式之前*不會*釋放全域性直譯器鎖,因為在呼叫直譯器程式碼時必須持有該鎖。有一個 py_object 型別建構函式將建立一個 PyObject* 指標。一個簡單的用法

import ctypes

d = {}
ctypes.pythonapi.PyObject_SetItem(ctypes.py_object(d),
          ctypes.py_object("abc"),  ctypes.py_object(1))
# d is now {'abc', 1}.

不要忘記使用 py_object();如果省略,您將遇到段錯誤。

ctypes 已經存在一段時間了,但人們仍然編寫和分發手工編碼的擴充套件模組,因為您不能依賴 ctypes 的存在。也許現在 ctypes 包含在核心 Python 中,開發者將開始在透過 ctypes 訪問的庫之上編寫 Python 包裝器,而不是擴充套件模組。

參見

https://web.archive.org/web/20180410025338/http://starship.python.net/crew/theller/ctypes/

pre-stdlib ctypes 網頁,包含教程、參考和常見問題解答。

ctypes 模組的文件。

ElementTree 包

Fredrik Lundh 的 ElementTree XML 處理庫的一個子集已作為 xml.etree 新增到標準庫中。可用的模組是 ElementTree 1.2.6 中的 ElementTreeElementPathElementInclude。還包括 cElementTree 加速模組。

本節的其餘部分將簡要概述 ElementTree 的使用。ElementTree 的完整文件可在 https://web.archive.org/web/20201124024954/http://effbot.org/zone/element-index.htm 獲取。

ElementTree 將 XML 文件表示為元素節點的樹。文件的文字內容儲存為 texttail 屬性。(這是 ElementTree 和文件物件模型之間的主要區別之一;在 DOM 中有許多不同型別的節點,包括 TextNode。)

最常用的解析函式是 parse(),它接受字串(假定包含檔名)或類似檔案的物件,並返回 ElementTree 例項

from xml.etree import ElementTree as ET

tree = ET.parse('ex-1.xml')

feed = urllib.urlopen(
          'http://planet.python.org/rss10.xml')
tree = ET.parse(feed)

一旦有了 ElementTree 例項,就可以呼叫其 getroot() 方法來獲取根 Element 節點。

還有一個 XML() 函式,它接受字串文字並返回一個 Element 節點(而不是 ElementTree)。此函式提供了一種整潔的方式來合併 XML 片段,接近 XML 文字的便利性

svg = ET.XML("""<svg width="10px" version="1.0">
             </svg>""")
svg.set('height', '320px')
svg.append(elem1)

每個 XML 元素都支援一些類似字典和類似列表的訪問方法。類似字典的操作用於訪問屬性值,類似列表的操作用於訪問子節點。

操作

結果

elem[n]

返回第 n 個子元素。

elem[m:n]

返回第 m 個到第 n 個子元素的列表。

len(elem)

返回子元素的數量。

list(elem)

返回子元素的列表。

elem.append(elem2)

新增 elem2 作為子元素。

elem.insert(index, elem2)

在指定位置插入 elem2

del elem[n]

刪除第 n 個子元素。

elem.keys()

返回屬性名稱列表。

elem.get(name)

返回屬性 name 的值。

elem.set(name, value)

設定屬性 name 的新值。

elem.attrib

檢索包含屬性的字典。

del elem.attrib[name]

刪除屬性 name

註釋和處理指令也表示為 Element 節點。要檢查節點是註釋還是處理指令

if elem.tag is ET.Comment:
    ...
elif elem.tag is ET.ProcessingInstruction:
    ...

要生成 XML 輸出,您應該呼叫 ElementTree.write() 方法。像 parse() 一樣,它可以接受字串或類似檔案的物件

# Encoding is US-ASCII
tree.write('output.xml')

# Encoding is UTF-8
f = open('output.xml', 'w')
tree.write(f, encoding='utf-8')

(注意:輸出使用的預設編碼是 ASCII。對於一般 XML 工作,其中元素的名稱可能包含任意 Unicode 字元,ASCII 不是一個非常有用的編碼,因為它會引發異常,如果元素的名稱包含任何值大於 127 的字元。因此,最好指定不同的編碼,例如 UTF-8,它可以處理任何 Unicode 字元。)

本節僅是對 ElementTree 介面的部分描述。請閱讀該包的官方文件以獲取更多詳細資訊。

hashlib 包

一個新的 hashlib 模組,由 Gregory P. Smith 編寫,已新增以替換 md5sha 模組。hashlib 增加了對其他安全雜湊(SHA-224、SHA-256、SHA-384 和 SHA-512)的支援。如果可用,該模組使用 OpenSSL 來實現快速平臺最佳化的演算法。

舊的 md5sha 模組仍然作為 hashlib 的包裝器存在,以保持向後相容性。新模組的介面與舊模組非常接近,但不完全相同。最顯著的區別是建立新雜湊物件的建構函式名稱不同。

# Old versions
h = md5.md5()
h = md5.new()

# New version
h = hashlib.md5()

# Old versions
h = sha.sha()
h = sha.new()

# New version
h = hashlib.sha1()

# Hash that weren't previously available
h = hashlib.sha224()
h = hashlib.sha256()
h = hashlib.sha384()
h = hashlib.sha512()

# Alternative form
h = hashlib.new('md5')          # Provide algorithm as a string

一旦建立了雜湊物件,它的方法與以前相同:update(string) 將指定的字串雜湊到當前摘要狀態,digest()hexdigest() 以二進位制字串或十六進位制數字字串的形式返回摘要值,copy() 返回一個具有相同摘要狀態的新雜湊物件。

參見

有關 hashlib 模組的文件。

sqlite3 包

pysqlite 模組(https://www.pysqlite.org),一個 SQLite 嵌入式資料庫的封裝,已以 sqlite3 的包名新增到標準庫中。

SQLite 是一個 C 庫,提供了一個輕量級的基於磁碟的資料庫,不需要單獨的伺服器程序,並允許使用非標準變體的 SQL 查詢語言訪問資料庫。有些應用程式可以使用 SQLite 進行內部資料儲存。也可以使用 SQLite 構建應用程式原型,然後將程式碼移植到更大的資料庫,如 PostgreSQL 或 Oracle。

pysqlite 由 Gerhard Häring 編寫,提供了一個符合 PEP 249 中描述的 DB-API 2.0 規範的 SQL 介面。

如果您自己編譯 Python 原始碼,請注意原始碼樹不包含 SQLite 程式碼,只包含封裝模組。您需要在編譯 Python 之前安裝 SQLite 庫和標頭檔案,並且在必要的標頭檔案可用時,構建過程將編譯該模組。

要使用此模組,您必須首先建立一個表示資料庫的 Connection 物件。這裡資料將儲存在 /tmp/example 檔案中

conn = sqlite3.connect('/tmp/example')

您還可以提供特殊名稱 :memory: 以在 RAM 中建立資料庫。

一旦您有了 Connection,您就可以建立一個 Cursor 物件,並呼叫其 execute() 方法來執行 SQL 命令

c = conn.cursor()

# Create table
c.execute('''create table stocks
(date text, trans text, symbol text,
 qty real, price real)''')

# Insert a row of data
c.execute("""insert into stocks
          values ('2006-01-05','BUY','RHAT',100,35.14)""")

通常,您的 SQL 操作需要使用 Python 變數中的值。您不應該使用 Python 的字串操作來組裝查詢,因為這樣做不安全;它使您的程式容易受到 SQL 注入攻擊。

相反,請使用 DB-API 的引數替換。在您想使用值的地方放置 ? 作為佔位符,然後提供一個值元組作為遊標的 execute() 方法的第二個引數。(其他資料庫模組可能使用不同的佔位符,例如 %s:1。)例如

# Never do this -- insecure!
symbol = 'IBM'
c.execute("... where symbol = '%s'" % symbol)

# Do this instead
t = (symbol,)
c.execute('select * from stocks where symbol=?', t)

# Larger example
for t in (('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
          ('2006-04-05', 'BUY', 'MSOFT', 1000, 72.00),
          ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
         ):
    c.execute('insert into stocks values (?,?,?,?,?)', t)

在執行 SELECT 語句後檢索資料,您可以將遊標視為迭代器,呼叫遊標的 fetchone() 方法來檢索單個匹配行,或呼叫 fetchall() 來獲取匹配行的列表。

此示例使用迭代器形式

>>> c = conn.cursor()
>>> c.execute('select * from stocks order by price')
>>> for row in c:
...    print row
...
(u'2006-01-05', u'BUY', u'RHAT', 100, 35.140000000000001)
(u'2006-03-28', u'BUY', u'IBM', 1000, 45.0)
(u'2006-04-06', u'SELL', u'IBM', 500, 53.0)
(u'2006-04-05', u'BUY', u'MSOFT', 1000, 72.0)
>>>

有關 SQLite 支援的 SQL 方言的更多資訊,請參閱 https://www.sqlite.org

參見

https://www.pysqlite.org

pysqlite 網頁。

https://www.sqlite.org

SQLite 網頁;文件描述了支援的 SQL 方言的語法和可用資料型別。

有關 sqlite3 模組的文件。

PEP 249 - 資料庫 API 規範 2.0

由 Marc-André Lemburg 編寫的 PEP。

wsgiref 包

Web Server Gateway Interface (WSGI) v1.0 定義了 Web 伺服器和 Python Web 應用程式之間的標準介面,並在 PEP 333 中有所描述。wsgiref 包是 WSGI 規範的參考實現。

該包包含一個將執行 WSGI 應用程式的基本 HTTP 伺服器;該伺服器對於除錯很有用,但不適用於生產環境。設定伺服器只需要幾行程式碼

from wsgiref import simple_server

wsgi_app = ...

host = ''
port = 8000
httpd = simple_server.make_server(host, port, wsgi_app)
httpd.serve_forever()

參見

https://web.archive.org/web/20160331090247/http://wsgi.readthedocs.org/en/latest/

WSGI 相關資源的中心網站。

PEP 333 - Python Web 伺服器閘道器介面 v1.0

由 Phillip J. Eby 編寫的 PEP。

構建和 C API 更改

Python 的構建過程和 C API 的更改包括

  • Python 原始碼樹從 CVS 轉換到 Subversion,這是一個複雜的遷移過程,由 Martin von Löwis 監督並完美執行。該過程被開發為 PEP 347

  • Coverity 是一家銷售名為 Prevent 的原始碼分析工具的公司,提供了他們對 Python 原始碼的檢查結果。分析發現了大約 60 個錯誤,這些錯誤很快得到了修復。許多錯誤是引用計數問題,通常發生在錯誤處理程式碼中。有關統計資料,請參見 https://scan.coverity.com

  • C API 最大的變化來自 PEP 353,它修改瞭解釋器以使用 Py_ssize_t 型別定義而不是 int。有關此變化的討論,請參見前面一節 PEP 353:使用 ssize_t 作為索引型別

  • 位元組碼編譯器的設計發生了很大變化,不再透過遍歷解析樹生成位元組碼。相反,解析樹被轉換為抽象語法樹(或 AST),然後遍歷抽象語法樹以生成位元組碼。

    Python 程式碼可以透過使用內建的 compile() 並指定 _ast.PyCF_ONLY_AST 作為 flags 引數的值來獲取 AST 物件

    from _ast import PyCF_ONLY_AST
    ast = compile("""a=0
    for i in range(10):
        a += i
    """, "<string>", 'exec', PyCF_ONLY_AST)
    
    assignment = ast.body[0]
    for_loop = ast.body[1]
    

    目前還沒有 AST 程式碼的官方文件,但 PEP 339 討論了其設計。要開始學習程式碼,請閱讀 Parser/Python.asdl 中各種 AST 節點的定義。一個 Python 指令碼讀取此檔案並在 Include/Python-ast.h 中生成一組 C 結構定義。在 Include/pythonrun.h 中定義的 PyParser_ASTFromString()PyParser_ASTFromFile() 將 Python 原始碼作為輸入,並返回表示內容的 AST 根。然後,此 AST 可以透過 PyAST_Compile() 轉換為程式碼物件。有關更多資訊,請閱讀原始碼,然後向 python-dev 提問。

    AST 程式碼是在 Jeremy Hylton 的管理下開發的,由(按字母順序)Brett Cannon、Nick Coghlan、Grant Edwards、John Ehresman、Kurt Kaiser、Neal Norwitz、Tim Peters、Armin Rigo 和 Neil Schemenauer,以及在 PyCon 等會議上多次 AST 短訓班的參與者實現。

  • Evan Jones 對 obmalloc 的補丁,最初在 PyCon DC 2005 的演講中描述,已被應用。Python 2.4 在 256K 大小的競技場中分配小物件,但從不釋放競技場。有了這個補丁,Python 將在競技場為空時釋放它們。最終結果是,在某些平臺上,當您分配許多物件時,當您刪除它們時,Python 的記憶體使用量實際上可能會下降,並且記憶體可能會返回給作業系統。(由 Evan Jones 實現,並由 Tim Peters 重新編寫。)

    請注意,此更改意味著擴充套件模組在分配記憶體時必須更加小心。Python 的 API 有許多不同的記憶體分配函式,它們被分組到家族中。例如,PyMem_Malloc()PyMem_Realloc()PyMem_Free() 是一個分配原始記憶體的家族,而 PyObject_Malloc()PyObject_Realloc()PyObject_Free() 是另一個用於建立 Python 物件的家族。

    以前,這些不同的家族都簡化為平臺的 malloc()free() 函式。這意味著如果你弄錯了,用 PyMem 函式分配記憶體,但用 PyObject 函式釋放,這無關緊要。隨著 2.5 對 obmalloc 的更改,這些家族現在執行不同的操作,不匹配可能會導致段錯誤。您應該仔細測試您的 C 擴充套件模組與 Python 2.5 的相容性。

  • 內建的集合型別現在有一個官方的 C API。呼叫 PySet_New()PyFrozenSet_New() 建立一個新集合,PySet_Add()PySet_Discard() 新增和刪除元素,以及 PySet_Contains()PySet_Size() 檢查集合的狀態。(由 Raymond Hettinger 貢獻。)

  • C 程式碼現在可以透過呼叫 Py_GetBuildInfo() 函式來獲取 Python 直譯器的確切修訂資訊,該函式返回一個構建資訊字串,例如:"trunk:45355:45356M, Apr 13 2006, 07:42:19"。(由 Barry Warsaw 貢獻。)

  • 可以使用兩個新宏來指示當前檔案本地的 C 函式,以便可以使用更快的呼叫約定。Py_LOCAL(type) 宣告函式返回指定 type 的值並使用快速呼叫限定符。Py_LOCAL_INLINE(type) 執行相同的操作並請求函式內聯。如果在包含 python.h 之前定義了宏 PY_LOCAL_AGGRESSIVE,則為模組啟用一組更積極的最佳化;您應該對結果進行基準測試,以找出這些最佳化是否真的使程式碼更快。(由 Fredrik Lundh 在 NeedForSpeed 衝刺中貢獻。)

  • PyErr_NewException(name, base, dict) 現在可以接受基類元組作為其 base 引數。(由 Georg Brandl 貢獻。)

  • 用於發出警告的 PyErr_Warn() 函式現在已棄用,取而代之的是 PyErr_WarnEx(category, message, stacklevel),它允許您指定此函式和呼叫方之間堆疊幀的數量。stacklevel 為 1 是呼叫 PyErr_WarnEx() 的函式,2 是其上方的函式,依此類推。(由 Neal Norwitz 新增。)

  • CPython 直譯器仍然用 C 編寫,但現在可以用 C++ 編譯器編譯而不會出錯。(由 Anthony Baxter、Martin von Löwis、Skip Montanaro 實現。)

  • 刪除了 PyRange_New() 函式。它從未被文件化,從未在核心程式碼中使用,並且具有危險的寬鬆錯誤檢查。在您的擴充套件使用它的不太可能的情況下,您可以將其替換為以下內容

    range = PyObject_CallFunction((PyObject*) &PyRange_Type, "lll",
                                  start, stop, step);
    

特定於埠的更改

  • MacOS X(10.3 及更高版本):模組的動態載入現在使用 dlopen() 函式而不是 MacOS 特定的函式。

  • MacOS X:configure 指令碼添加了一個 --enable-universalsdk 開關,用於將直譯器編譯為可在 PowerPC 和 Intel 處理器上執行的通用二進位制檔案。(由 Ronald Oussoren 貢獻;bpo-2573。)

  • Windows:.dll 不再支援作為擴充套件模組的檔名擴充套件。現在 .pyd 是唯一將被搜尋的檔名擴充套件。

移植到 Python 2.5

本節列出了可能需要更改程式碼的先前描述的更改

  • ASCII 現在是模組的預設編碼。如果模組包含帶有 8 位字元的字串文字但沒有編碼宣告,則現在是語法錯誤。在 Python 2.4 中,這會觸發警告,而不是語法錯誤。

  • 以前,生成器的 gi_frame 屬性始終是一個幀物件。由於 PEP 342:新生成器功能 一節中描述的 PEP 342 更改,現在 gi_frame 可能為 None

  • 當您嘗試比較 Unicode 字串和無法使用預設 ASCII 編碼轉換為 Unicode 的 8 位字串時,會觸發一個新的警告,UnicodeWarning。以前,此類比較會引發 UnicodeDecodeError 異常。

  • 庫:csv 模組現在對多行帶引號的欄位更加嚴格。如果您的檔案中包含欄位內嵌的換行符,則輸入應以保留換行符的方式拆分成行。

  • 庫:locale 模組的 format() 函式以前接受任何字串,只要只出現一個 %char 格式說明符。在 Python 2.5 中,引數必須是恰好一個 %char 格式說明符,不帶周圍文字。

  • 庫:picklecPickle 模組不再接受來自 __reduce__() 方法的 None 返回值;該方法必須返回一個引數元組。這些模組也不再接受已棄用的 bin 關鍵字引數。

  • 庫:SimpleXMLRPCServerDocXMLRPCServer 類現在有一個 rpc_paths 屬性,它將 XML-RPC 操作限制在有限的 URL 路徑集上;預設情況下只允許 '/''/RPC2'。將 rpc_paths 設定為 None 或空元組會停用此路徑檢查。

  • C API:許多函式現在使用 Py_ssize_t 而不是 int,以便在 64 位機器上處理更多資料。擴充套件程式碼可能需要進行相同的更改,以避免警告並支援 64 位機器。有關此更改的討論,請參見前面一節 PEP 353:使用 ssize_t 作為索引型別

  • C API:obmalloc 的更改意味著您必須小心,不要混用 PyMem_*PyObject_* 函式家族。使用一個家族的 *_Malloc 分配的記憶體必須使用相應家族的 *_Free 函式釋放。

致謝

作者感謝以下人員對本文各種草稿提出建議、更正和協助:Georg Brandl、Nick Coghlan、Phillip J. Eby、Lars Gustäbel、Raymond Hettinger、Ralf W. Grosse-Kunstleve、Kent Johnson、Iain Lowe、Martin von Löwis、Fredrik Lundh、Andrew McNamara、Skip Montanaro、Gustavo Niemeyer、Paul Prescod、James Pryor、Mike Rovner、Scott Weikart、Barry Warsaw、Thomas Wouters。