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:條件表示式

長期以來,人們一直要求一種編寫條件表示式的方法,條件表示式是根據布林值為 true 或 false 返回值 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

評估仍然像在現有的布林表示式中一樣是惰性的,因此評估的順序會稍微跳躍。首先評估中間的condition表示式,並且僅當條件為真時才評估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 - 條件表示式

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

PEP 309:偏函式應用

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

此模組中的一個有用工具是 partial() 函式。對於以函式式風格編寫的程式,有時您會想要構造現有函式的變體,其中填充了一些引數。考慮一個 Python 函式 f(a, b, c);您可以建立一個與 f(1, b, c) 等效的新函式 g(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),
     )

https://pypi.org 的 Python 軟體包索引中的另一個新增強功能是儲存軟體包的原始碼和二進位制存檔。新的 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:絕對和相對匯入

在 Python 2.4 中實現了 PEP 328 中較簡單的部分:現在可以使用圓括號將從模組匯入的名稱括起來,使用 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 安裝中都可用。

閱讀依賴相對匯入的程式碼也不太清楚,因為讀者可能會對打算使用哪個模組感到困惑,string 還是 pkg.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 形式的匯入語句一起使用,只能與 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 - 將模組作為指令碼執行

此 PEP 由 Nick Coghlan 編寫和實現。

PEP 341:統一的 try/except/finally

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

Guido van Rossum 花了一些時間使用 Java,Java 確實支援組合 except 塊和 finally 塊的等效項,這澄清了該語句應該意味著什麼。在 Python 2.5 中,您現在可以編寫

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

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

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

另請參閱

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

Georg Brandl 撰寫的 PEP;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 異常以終止迭代。在收到此異常時,生成器的程式碼必須引發 GeneratorExitStopIteration。捕獲 GeneratorExit 異常並返回值是非法的,將觸發 RuntimeError;如果該函式引發其他異常,則該異常會傳播到呼叫者。close() 還會在生成器被垃圾回收時由 Python 的垃圾回收器呼叫。

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

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

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

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

此更改的另一個更深奧的影響:以前,生成器的 gi_frame 屬性始終是一個幀物件。現在,一旦生成器耗盡,gi_frame 就有可能為 None

另請參閱

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

Guido van Rossum 和 Phillip J. Eby 撰寫的 PEP;由 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 不會 被賦予 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 ...

在此語句執行後,f 中的檔案物件將自動關閉,即使 for 迴圈在程式碼塊中途引發了異常。

注意

在這種情況下,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 引發異常,則會使用異常詳細資訊(與 sys.exc_info() 返回的相同值)呼叫 __exit__(type, value, traceback) 方法。該方法的返回值控制是否重新引發異常:任何假值都會重新引發異常,而 True 將導致抑制異常。您很少需要抑制異常,因為如果您這樣做,則包含 ‘with’ 語句的程式碼作者將永遠不會意識到出現了問題。

  • 如果 BLOCK 沒有引發異常,則仍會呼叫 __exit__() 方法,但 typevaluetraceback 都為 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” 語句

由 Guido van Rossum 和 Nick Coghlan 編寫的 PEP;由 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 3.0 的目標是要求任何作為異常引發的類都必須派生自 BaseExceptionBaseException 的某個後代,並且 Python 2.x 系列的未來版本可能會開始強制執行此約束。因此,我建議您現在開始使所有異常類都派生自 Exception。有人建議應該在 Python 3.0 中刪除裸的 except: 形式,但 Guido van Rossum 尚未決定是否這樣做。

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

另請參閱

PEP 352 - 異常所需的超類

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

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

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

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 作為索引型別

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

PEP 357:’__index__’ 方法

NumPy 開發人員遇到了一個只能透過新增新的特殊方法 __index__() 來解決的問題。使用切片表示法(如 [start:stop:step] 中),startstopstep 索引的值都必須是整數或長整數。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 - 允許將任何物件用於切片

由 Travis Oliphant 編寫和實現的 PEP。

其他語言更改

以下是 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 代表“反向”。

    一些例子

    >>> ('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() 內建函式獲得了 key 關鍵字引數,類似於 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。比較的結果為假

    >>> 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 sprint 貢獻。)

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

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

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

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

  • 匯入現在會快取嘗試過的路徑,記錄它們是否存在,以便直譯器在啟動時減少對 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() 函式現在接受一個可選的 generation 引數,其值為 0、1 或 2,用於指定要收集的代。(由 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 年程式設計之夏提供。)

  • 新模組: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 作為輸入,因此它等待任何子程序退出,並返回一個 3 元組,其中包含從 resource.getrusage() 函式返回的 process-idexit-statusresource-usagewait4(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 貢獻。)

  • 自 Python 2.0 以來就被棄用的舊 regexregsub 模組終於被刪除。其他刪除的模組:statcachetzparsewhrandom

  • 同樣被刪除的:包括 dircmpni 等古老模組的 lib-old 目錄被刪除。lib-old 不在預設的 sys.path 上,因此除非你的程式顯式地將該目錄新增到 sys.path 中,否則此刪除不應影響你的程式碼。

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

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

  • 由於 Philippe Biondi 的補丁,socket 模組現在在 Linux 上支援 AF_NETLINK 套接字。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 中新增。)

  • 在 2.5 的開發過程中,Python 開發人員從 CVS 切換到了 Subversion。關於確切構建版本的資訊可以作為 sys.subversion 變數使用,這是一個由 (直譯器名稱, 分支名稱, 修訂範圍) 組成的 3 元組。例如,在撰寫本文時,我的 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() 方法已新增到 WeakKeyDictionary,而 itervaluerefs()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 還為 Python 的 C API 提供了包裝器,作為 ctypes.pythonapi 物件。此物件在呼叫函式之前不會釋放全域性直譯器鎖,因為在呼叫直譯器程式碼時必須持有該鎖。有一個 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/

預先新增到標準庫的 ctypes 網頁,包含教程、參考和常見問題解答。

ctypes 模組的文件。

ElementTree 包

Fredrik Lundh 的 ElementTree 庫的一個子集,用於處理 XML,已作為 xml.etree 新增到標準庫中。可用的模組是 ElementTree 1.2.6 中的 ElementTreeElementPathElementIncludecElementTree 加速模組也包含在內。

本節的其餘部分將簡要概述 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 的字元,它會引發異常。因此,最好指定不同的編碼,例如可以處理任何 Unicode 字元的 UTF-8。)

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

hashlib 包

Gregory P. Smith 編寫的新的 hashlib 模組已新增,以替換 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 伺服器閘道器介面 (WSGI) v1.0 定義了 Web 伺服器和 Python Web 應用程式之間的標準介面,並在 PEP 333 中進行了描述。wsgiref 包是 WSGI 規範的參考實現。

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

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 結構定義。PyParser_ASTFromString()PyParser_ASTFromFile() 定義在 Include/pythonrun.h 中,它們將 Python 原始碼作為輸入並返回表示內容的 AST 的根。然後可以透過 PyAST_Compile() 將此 AST 轉換為程式碼物件。有關更多資訊,請閱讀原始碼,然後在 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 的補丁,該補丁首次在 2005 年的 PyCon DC 上的一次演講中進行了描述。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 的更改,這些系列現在執行不同的操作,並且不匹配可能會導致段錯誤。您應該使用 Python 2.5 仔細測試您的 C 擴充套件模組。

  • 內建集合型別現在具有官方的 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

  • 當您嘗試比較無法使用預設 ASCII 編碼轉換為 Unicode 的 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。