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_v
,if 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()
函式現在有 requires
、provides
和 obsoletes
關鍵字引數。當你使用 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.main
和 pkg.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
形式的 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__()
方法最複雜,因為大部分工作都必須在那裡完成。該方法必須檢查是否發生了異常。如果沒有異常,則提交事務。如果發生異常,則回滾事務。
在下面的程式碼中,執行將直接跳出函式,返回預設值 None
。None
為假,因此異常將自動重新引發。如果您願意,可以更明確地在標記位置新增一個 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
類和所有標準內建異常(NameError
、ValueError
等)現在都是新式類。
異常的繼承層次結構有所調整。在 2.5 中,繼承關係如下:
BaseException # New in Python 2.5
|- KeyboardInterrupt
|- SystemExit
|- Exception
|- (all other current built-in exceptions)
這種重新安排是因為人們經常希望捕獲所有指示程式錯誤的異常。KeyboardInterrupt
和 SystemExit
雖然不是錯誤,但通常表示明確的操作,例如使用者按下 Control-C 或程式碼呼叫 sys.exit()
。一個裸的 except:
會捕獲所有異常,因此通常你需要列出 KeyboardInterrupt
和 SystemExit
以便重新引發它們。通常的模式是:
try:
...
except (KeyboardInterrupt, SystemExit):
raise
except:
# Log error...
# Continue running program...
在 Python 2.5 中,你現在可以寫 except Exception
來達到相同的效果,捕獲通常指示錯誤的所有異常,但保留 KeyboardInterrupt
和 SystemExit
不動。與以前的版本一樣,一個裸的 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_t
。PyArg_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
>;否則返回False
。all()
僅當迭代器返回的所有值都評估為真時才返回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 實現。)
互動式直譯器更改¶
在互動式直譯器中,quit
和 exit
長期以來都是字串,以便新使用者在嘗試退出時獲得一些有用的訊息
>>> quit
'Use Ctrl-D (i.e. EOF) to exit.'
在 Python 2.5 中,quit
和 exit
現在是物件,它們仍然生成自身的字串表示,但也可以呼叫。嘗試 quit()
或 exit()
的新手現在將按預期退出直譯器。(由 Georg Brandl 實現。)
Python 可執行檔案現在接受標準長選項 --help
和 --version
;在 Windows 上,它還接受 /?
選項以顯示幫助訊息。(由 Georg Brandl 實現。)
最佳化¶
其中一些最佳化是在冰島雷克雅未克於 2006 年 5 月 21 日至 28 日舉行的 NeedForSpeed 衝刺活動中開發的。該衝刺活動主要關注 CPython 實現的速度提升,由 EWT LLC 資助,並得到 CCP Games 的當地支援。在此衝刺活動中新增的最佳化在以下列表中特別標註。
當在 Python 2.4 中引入時,內建的
set
和frozenset
型別是建立在 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 元組以保持向後相容性,但也具有encode
、decode
、incrementalencoder
、incrementaldecoder
、streamwriter
和streamreader
屬性。增量編解碼器可以接收輸入並以多個塊生成輸出;輸出與將整個輸入提供給非增量編解碼器相同。有關詳細資訊,請參閱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
模組經過了大規模重寫,除了讀取郵箱之外,還增加了修改郵箱的功能。一套新的類,包括mbox
、MH
和Maildir
,用於讀取郵箱,並具有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')
將返回一個檢索a
和b
屬性的函式。將此新功能與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_SET
、os.SEEK_CUR
和os.SEEK_END
的常量;這些是os.lseek()
函式的引數。兩個新的鎖定常量是os.O_SHLOCK
和os.O_EXLOCK
。添加了兩個新函式:
wait3()
和wait4()
。它們與waitpid()
函式類似,後者等待子程序退出並返回程序 ID 及其退出狀態的元組,但wait3()
和wait4()
返回附加資訊。wait3()
不接受程序 ID 作為輸入,因此它等待任何子程序退出並返回 程序 ID、退出狀態、資源使用情況 的 3 元組,其值由resource.getrusage()
函式返回。wait4(pid)
確實接受程序 ID。(由 Chad J. Schroeder 貢獻。)在 FreeBSD 上,
os.stat()
函式現在返回納秒級精度的時間,並且返回的物件現在具有st_gen
和st_birthtime
屬性。如果平臺支援,st_flags
屬性也可用。(由 Antti Louko 和 Diego Pettenò 貢獻。)pdb
模組提供的 Python 偵錯程式現在可以儲存命令列表,以便在達到斷點並停止執行時執行。建立斷點 #1 後,輸入commands 1
並輸入一系列要執行的命令,以end
結束列表。命令列表可以包含恢復執行的命令,例如continue
或next
。(由 Grégoire Dooms 貢獻。)pickle
和cPickle
模組不再接受__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 貢獻。)舊的
regex
和regsub
模組(自 Python 2.0 以來已棄用)最終已被刪除。其他已刪除的模組:statcache
、tzparse
、whrandom
。同樣刪除的還有
lib-old
目錄,其中包含一些舊模組,例如dircmp
和ni
。lib-old
不在預設的sys.path
中,因此除非您的程式明確將該目錄新增到sys.path
,否則此刪除應該不會影響您的程式碼。rlcompleter
模組不再依賴於匯入readline
模組,因此現在可以在非 Unix 平臺上工作。(來自 Robert Kiendl 的補丁。)SimpleXMLRPCServer
和DocXMLRPCServer
類現在具有一個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
模組的WeakKeyDictionary
和WeakValueDictionary
型別獲得了用於迭代字典中包含的弱引用的新方法。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
模組的Compress
和Decompress
物件現在支援copy()
方法,該方法複製物件的內部狀態並返回一個新的Compress
或Decompress
物件。(由 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 中的 ElementTree
、ElementPath
和 ElementInclude
。還包括 cElementTree
加速模組。
本節的其餘部分將簡要概述 ElementTree 的使用。ElementTree 的完整文件可在 https://web.archive.org/web/20201124024954/http://effbot.org/zone/element-index.htm 獲取。
ElementTree 將 XML 文件表示為元素節點的樹。文件的文字內容儲存為 text
和 tail
屬性。(這是 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 元素都支援一些類似字典和類似列表的訪問方法。類似字典的操作用於訪問屬性值,類似列表的操作用於訪問子節點。
操作 |
結果 |
---|---|
|
返回第 n 個子元素。 |
|
返回第 m 個到第 n 個子元素的列表。 |
|
返回子元素的數量。 |
|
返回子元素的列表。 |
|
新增 elem2 作為子元素。 |
|
在指定位置插入 elem2。 |
|
刪除第 n 個子元素。 |
|
返回屬性名稱列表。 |
|
返回屬性 name 的值。 |
|
設定屬性 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 編寫,已新增以替換 md5
和 sha
模組。hashlib
增加了對其他安全雜湊(SHA-224、SHA-256、SHA-384 和 SHA-512)的支援。如果可用,該模組使用 OpenSSL 來實現快速平臺最佳化的演算法。
舊的 md5
和 sha
模組仍然作為 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 格式說明符,不帶周圍文字。庫:
pickle
和cPickle
模組不再接受來自__reduce__()
方法的None
返回值;該方法必須返回一個引數元組。這些模組也不再接受已棄用的 bin 關鍵字引數。庫:
SimpleXMLRPCServer
和DocXMLRPCServer
類現在有一個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。