Python 2.3 中的新功能¶
- 作者:
A.M. Kuchling
本文解釋了 Python 2.3 中的新功能。Python 2.3 於 2003 年 7 月 29 日釋出。
Python 2.3 的主要主題是完善 2.2 中新增的一些功能,向核心語言新增各種小型但有用的增強功能,以及擴充套件標準庫。上一個版本中引入的新物件模型受益於 18 個月的錯誤修復和最佳化工作,這些工作改進了新式類的效能。添加了一些新的內建函式,例如 sum()
和 enumerate()
。in
運算子現在可用於子字串搜尋(例如 "ab" in "abc"
返回 True
)。
許多新的庫功能包括布林、集合、堆和日期/時間資料型別,從 ZIP 格式存檔匯入模組的能力,對期待已久的 Python 目錄的元資料支援,更新版本的 IDLE,以及用於記錄訊息、文字換行、解析 CSV 檔案、處理命令列選項、使用 BerkeleyDB 資料庫的模組……新增和增強模組的列表很長。
本文不打算提供新功能的完整規範,而是提供一個方便的概述。有關完整詳細資訊,您應該參考 Python 2.3 的文件,例如 Python 庫參考和 Python 參考手冊。如果您想了解完整的實現和設計原理,請參考特定新功能的 PEP。
PEP 218: 標準集合資料型別¶
新的 sets
模組包含集合資料型別的實現。Set
類用於可變集合,即可以新增和刪除成員的集合。ImmutableSet
類用於不能修改的集合,因此 ImmutableSet
的例項可以用作字典鍵。集合是建立在字典之上的,因此集合中的元素必須是可雜湊的。
這是一個簡單的例子
>>> import sets
>>> S = sets.Set([1,2,3])
>>> S
Set([1, 2, 3])
>>> 1 in S
True
>>> 0 in S
False
>>> S.add(5)
>>> S.remove(3)
>>> S
Set([1, 2, 5])
>>>
集合的並集和交集可以使用 union()
和 intersection()
方法計算;另一種表示法使用位運算子 &
和 |
。可變集合也具有這些方法的就地版本,union_update()
和 intersection_update()
。
>>> S1 = sets.Set([1,2,3])
>>> S2 = sets.Set([4,5,6])
>>> S1.union(S2)
Set([1, 2, 3, 4, 5, 6])
>>> S1 | S2 # Alternative notation
Set([1, 2, 3, 4, 5, 6])
>>> S1.intersection(S2)
Set([])
>>> S1 & S2 # Alternative notation
Set([])
>>> S1.union_update(S2)
>>> S1
Set([1, 2, 3, 4, 5, 6])
>>>
也可以計算兩個集合的對稱差集。這是並集中不在交集中的所有元素的集合。另一種說法是,對稱差集包含恰好在一個集合中的所有元素。同樣,還有另一種表示法(^
),以及一個名稱難聽的就地版本 symmetric_difference_update()
。
>>> S1 = sets.Set([1,2,3,4])
>>> S2 = sets.Set([3,4,5,6])
>>> S1.symmetric_difference(S2)
Set([1, 2, 5, 6])
>>> S1 ^ S2
Set([1, 2, 5, 6])
>>>
還有 issubset()
和 issuperset()
方法用於檢查一個集合是否是另一個集合的子集或超集
>>> S1 = sets.Set([1,2,3])
>>> S2 = sets.Set([2,3])
>>> S2.issubset(S1)
True
>>> S1.issubset(S2)
False
>>> S1.issuperset(S2)
True
>>>
參見
- PEP 218 - 新增內建集合物件型別
PEP 由 Greg V. Wilson 撰寫。由 Greg V. Wilson、Alex Martelli 和 GvR 實現。
PEP 255:簡單生成器¶
在 Python 2.2 中,生成器作為可選功能新增,透過 from __future__ import generators
指令啟用。在 2.3 中,生成器不再需要特殊啟用,並且現在總是存在;這意味著 yield
現在總是關鍵字。本節的其餘部分是“Python 2.2 中的新功能”文件中生成器描述的副本;如果您在 Python 2.2 釋出時閱讀過它,則可以跳過本節的其餘部分。
您無疑熟悉 Python 或 C 中函式呼叫如何工作。當您呼叫一個函式時,它會獲得一個私有名稱空間,其中建立其區域性變數。當函式到達 return
語句時,區域性變數被銷燬,結果值返回給呼叫者。稍後對同一函式的呼叫將獲得一組全新的區域性變數。但是,如果區域性變數在函式退出時沒有被丟棄怎麼辦?如果以後可以從函式中斷的地方恢復怎麼辦?這就是生成器提供的;它們可以被認為是可恢復的函式。
這是一個生成器函式最簡單的例子:
def generate_ints(N):
for i in range(N):
yield i
為生成器引入了一個新關鍵字 yield
。任何包含 yield
語句的函式都是生成器函式;這由 Python 的位元組碼編譯器檢測到,因此會特殊編譯該函式。
當你呼叫一個生成器函式時,它不會返回一個單一的值;相反,它返回一個支援迭代器協議的生成器物件。執行 yield
語句時,生成器輸出 i
的值,類似於 return
語句。yield
和 return
語句之間的最大區別在於,到達 yield
時,生成器的執行狀態被掛起,區域性變數被保留。在下一次呼叫生成器的 .next()
方法時,函式將立即在 yield
語句之後恢復執行。(由於複雜的原因,yield
語句不允許在 try
...finally
語句的 try
塊內;請閱讀 PEP 255 以獲取 yield
與異常之間互動的完整解釋。)
以下是 generate_ints()
生成器的示例用法
>>> gen = generate_ints(3)
>>> gen
<generator object at 0x8117f90>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "stdin", line 1, in ?
File "stdin", line 2, in generate_ints
StopIteration
您也可以寫成 for i in generate_ints(5)
,或者 a,b,c = generate_ints(3)
。
在生成器函式內部,return
語句只能不帶值使用,表示值序列的結束;此後生成器不能返回任何進一步的值。帶有值的 return
,例如 return 5
,在生成器函式內部是語法錯誤。生成器結果的結束也可以透過手動引發 StopIteration
,或者僅僅讓執行流程離開函式的底部來表示。
您可以透過編寫自己的類並將生成器的所有區域性變數儲存為例項變數來手動實現生成器的效果。例如,返回整數列表可以透過將 self.count
設定為 0,並讓 next()
方法遞增 self.count
並返回它來完成。但是,對於一箇中等複雜的生成器,編寫相應的類會更加混亂。Lib/test/test_generators.py
包含許多更有趣的示例。最簡單的一個使用生成器遞迴地實現樹的遍歷。
# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
Lib/test/test_generators.py
中的另外兩個示例為 N 皇后問題(在 NxN 棋盤上放置 N 個皇后,使沒有任何皇后威脅另一個)和騎士之旅(騎士在 NxN 棋盤上訪問每個方格而不訪問任何方格兩次的路線)提供瞭解決方案。
生成器的概念來自其他程式語言,尤其是 Icon (https://www2.cs.arizona.edu/icon/),其中生成器的概念是核心。在 Icon 中,每個表示式和函式呼叫都像生成器一樣工作。來自 https://www2.cs.arizona.edu/icon/docs/ipd266.htm 的“Icon 程式語言概述”中的一個示例給出了它的樣子
sentence := "Store it in the neighboring harbor"
if (i := find("or", sentence)) > 5 then write(i)
在 Icon 中,find()
函式返回子字串“or”所在的索引:3、23、33。在 if
語句中,i
首先被賦值為 3,但 3 小於 5,因此比較失敗,Icon 嘗試使用第二個值 23。23 大於 5,因此比較成功,程式碼將值 23 列印到螢幕上。
Python 在將生成器作為核心概念方面並沒有像 Icon 那樣走得那麼遠。生成器被認為是 Python 核心語言的一部分,但學習或使用它們並不是強制性的;如果它們不能解決您遇到的任何問題,請隨意忽略它們。與 Icon 的介面相比,Python 介面的一個新穎之處在於生成器的狀態被表示為一個具體物件(迭代器),可以傳遞給其他函式或儲存在資料結構中。
參見
- PEP 255 - 簡單生成器
由 Neil Schemenauer、Tim Peters、Magnus Lie Hetland 撰寫。主要由 Neil Schemenauer 和 Tim Peters 實現,並由 Python Labs 團隊進行了其他修復。
PEP 263:原始碼編碼¶
Python 原始檔現在可以宣告為使用不同的字元集編碼。編碼透過在原始檔的第一行或第二行包含一個特殊格式的註釋來宣告。例如,UTF-8 檔案可以宣告為
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
如果沒有這樣的編碼宣告,預設編碼為 7 位 ASCII。執行或匯入包含 8 位字元的字串字面量且沒有編碼宣告的模組,將導致 Python 2.3 發出 DeprecationWarning
;在 2.4 中這將是一個語法錯誤。
編碼宣告隻影響 Unicode 字串字面量,它們將使用指定的編碼轉換為 Unicode。請注意,Python 識別符號仍然僅限於 ASCII 字元,因此您不能使用常規字母數字之外的字元作為變數名。
參見
- PEP 263 - 定義 Python 原始碼編碼
由 Marc-André Lemburg 和 Martin von Löwis 撰寫;由 Suzuki Hisao 和 Martin von Löwis 實現。
PEP 273:從 ZIP 存檔匯入模組¶
新的 zipimport
模組增加了從 ZIP 格式存檔匯入模組的支援。您無需顯式匯入該模組;如果將 ZIP 存檔的檔名新增到 sys.path
,它將自動匯入。例如
amk@nyman:~/src/python$ unzip -l /tmp/example.zip
Archive: /tmp/example.zip
Length Date Time Name
-------- ---- ---- ----
8467 11-26-02 22:30 jwzthreading.py
-------- -------
8467 1 file
amk@nyman:~/src/python$ ./python
Python 2.3 (#1, Aug 1 2003, 19:54:32)
>>> import sys
>>> sys.path.insert(0, '/tmp/example.zip') # Add .zip file to front of path
>>> import jwzthreading
>>> jwzthreading.__file__
'/tmp/example.zip/jwzthreading.py'
>>>
sys.path
中的一個條目現在可以是 ZIP 存檔的檔名。ZIP 存檔可以包含任何型別的檔案,但只有名為 *.py
、*.pyc
或 *.pyo
的檔案才能被匯入。如果存檔只包含 *.py
檔案,Python 不會嘗試透過新增相應的 *.pyc
檔案來修改存檔,這意味著如果 ZIP 存檔不包含 *.pyc
檔案,匯入可能會相當慢。
也可以指定存檔內的路徑,僅從子目錄匯入;例如,路徑 /tmp/example.zip/lib/
將僅從存檔中的 lib/
子目錄匯入。
參見
- PEP 273 - 從 Zip 存檔匯入模組
由 James C. Ahlstrom 撰寫,他也提供了實現。Python 2.3 遵循 PEP 273 中的規範,但使用 Just van Rossum 編寫的實現,該實現使用了 PEP 302 中描述的匯入鉤子。有關新匯入鉤子的描述,請參閱 PEP 302: 新匯入鉤子 一節。
PEP 277:Windows NT 的 Unicode 檔名支援¶
在 Windows NT、2000 和 XP 上,系統將檔名儲存為 Unicode 字串。傳統上,Python 將檔案名錶示為位元組字串,這不足以訪問某些檔名。
Python 現在允許將任意 Unicode 字串(在檔案系統限制內)用於所有期望檔名的函式,最值得注意的是內建函式 open()
。如果將 Unicode 字串傳遞給 os.listdir()
,Python 現在會返回一個 Unicode 字串列表。一個新函式 os.getcwdu()
以 Unicode 字串的形式返回當前目錄。
位元組字串仍然可以用作檔名,在 Windows 上,Python 會使用 mbcs
編碼將其透明地轉換為 Unicode。
其他系統也允許 Unicode 字串作為檔名,但在將其傳遞給系統之前將其轉換為位元組字串,這可能會引發 UnicodeError
。應用程式可以透過檢查布林值 os.path.supports_unicode_filenames
來測試是否支援任意 Unicode 字串作為檔名。
在 MacOS 下,os.listdir()
現在可以返回 Unicode 檔名。
參見
- PEP 277 - Windows NT 的 Unicode 檔名支援
由 Neil Hodgson 撰寫;由 Neil Hodgson、Martin von Löwis 和 Mark Hammond 實現。
PEP 278:通用換行符支援¶
目前使用的三大作業系統是 Microsoft Windows、Apple 的 Macintosh OS 和各種 Unix 衍生版。跨平臺工作的一個小麻煩是這三個平臺都使用不同的字元來標記文字檔案中的行尾。Unix 使用換行符(ASCII 字元 10),MacOS 使用回車符(ASCII 字元 13),而 Windows 使用回車符加換行符的兩個字元序列。
Python 的檔案物件現在可以支援與 Python 執行平臺所遵循的約定不同的行尾約定。以模式 'U'
或 'rU'
開啟檔案將以 通用換行符 模式開啟檔案進行讀取。所有三種行尾約定都將被翻譯成 '\n'
,並由各種檔案方法(如 read()
和 readline()
)返回的字串中。
匯入模組和使用 execfile()
函式執行檔案時,也使用通用換行符支援。這意味著 Python 模組可以在所有三個作業系統之間共享,而無需轉換行尾。
在執行 Python 的 configure 指令碼時,可以透過指定 --without-universal-newlines
開關來停用此功能。
參見
- PEP 278 - 通用換行符支援
由 Jack Jansen 撰寫並實現。
PEP 279: enumerate()¶
一個新的內建函式 enumerate()
將使某些迴圈更加清晰。enumerate(thing)
,其中 *thing* 是迭代器或序列,返回一個迭代器,它將返回 (0, thing[0])
、(1, thing[1])
、(2, thing[2])
等等。
一種常見的修改列表每個元素的慣用語如下所示
for i in range(len(L)):
item = L[i]
# ... compute some result based on item ...
L[i] = result
這可以使用 enumerate()
重寫為
for i, item in enumerate(L):
# ... compute some result based on item ...
L[i] = result
參見
- PEP 279 - 內建函式 enumerate()
由 Raymond D. Hettinger 撰寫並實現。
PEP 282: logging 包¶
一個用於編寫日誌的標準包 logging
已新增到 Python 2.3 中。它提供了一個強大而靈活的機制,用於生成日誌輸出,然後可以透過各種方式對其進行過濾和處理。可以使用標準格式編寫的配置檔案來控制程式的日誌行為。Python 包含處理程式,可以將日誌記錄寫入標準錯誤或檔案或套接字,將其傳送到系統日誌,甚至將其透過電子郵件傳送到特定地址;當然,也可以編寫自己的處理程式類。
Logger
類是主要的類。大多數應用程式程式碼將處理一個或多個 Logger
物件,每個物件由應用程式的特定子系統使用。每個 Logger
都由一個名稱標識,名稱使用 .
作為元件分隔符組織成一個層次結構。例如,您可能擁有名為 server
、server.auth
和 server.network
的 Logger
例項。後兩個例項在層次結構中位於 server
之下。這意味著如果您提高 server
的詳細程度或將 server
訊息定向到不同的處理程式,這些更改也將應用於記錄到 server.auth
和 server.network
的記錄。還有一個根 Logger
,它是所有其他記錄器的父級。
對於簡單用途,logging
包包含一些始終使用根日誌的便捷函式
import logging
logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')
這會產生以下輸出:
WARNING:root:Warning:config file server.conf not found
ERROR:root:Error occurred
CRITICAL:root:Critical error -- shutting down
在預設配置中,資訊和除錯訊息被抑制,輸出傳送到標準錯誤。您可以透過呼叫根記錄器上的 setLevel()
方法來啟用資訊和除錯訊息的顯示。
請注意 warning()
呼叫使用了字串格式化運算子;所有用於記錄訊息的函式都接受引數 (msg, arg1, arg2, ...)
並記錄 msg % (arg1, arg2, ...)
產生的字串。
還有一個 exception()
函式,它記錄最近的追溯。如果您為關鍵字引數 *exc_info* 指定一個真值,任何其他函式也會記錄追溯。
def f():
try: 1/0
except: logging.exception('Problem recorded')
f()
這會產生以下輸出:
ERROR:root:Problem recorded
Traceback (most recent call last):
File "t.py", line 6, in f
1/0
ZeroDivisionError: integer division or modulo by zero
稍高階的程式將使用除根記錄器之外的記錄器。getLogger(name)
函式用於獲取特定日誌,如果它尚不存在則建立它。getLogger(None)
返回根記錄器。
log = logging.getLogger('server')
...
log.info('Listening on port %i', port)
...
log.critical('Disk full')
...
日誌記錄通常會沿層次結構向上傳播,因此記錄到 server.auth
的訊息也會被 server
和 root
看到,但 Logger
可以透過將其 propagate
屬性設定為 False
來阻止這種情況。
logging
包還提供了更多可自定義的類。當 Logger
例項被告知記錄訊息時,它會建立一個 LogRecord
例項,該例項會發送到任意數量的不同 Handler
例項。記錄器和處理程式還可以附加一個過濾器列表,每個過濾器都可以導致 LogRecord
被忽略,或者在將其傳遞之前修改記錄。當它們最終輸出時,LogRecord
例項由 Formatter
類轉換為文字。所有這些類都可以替換為您自己專門編寫的類。
憑藉所有這些功能,logging
包應該為最複雜的應用程式提供足夠的靈活性。這只是對其功能的不完整概述,因此請參閱該包的參考文件以獲取所有詳細資訊。閱讀 PEP 282 也會有所幫助。
參見
- PEP 282 - 日誌系統
由 Vinay Sajip 和 Trent Mick 撰寫;由 Vinay Sajip 實現。
PEP 285:布林型別¶
Python 2.3 中添加了布林型別。兩個新常量被新增到 __builtin__
模組中,True
和 False
。(True
和 False
常量已在 Python 2.2.1 中新增到內建函式中,但 2.2.1 版本只是簡單地設定為整數值 1 和 0,並不是不同的型別。)
此新型別的型別物件名為 bool
;它的建構函式接受任何 Python 值並將其轉換為 True
或 False
。
>>> bool(1)
True
>>> bool(0)
False
>>> bool([])
False
>>> bool( (1,) )
True
大多數標準庫模組和內建函式都已更改為返回布林值。
>>> obj = []
>>> hasattr(obj, 'append')
True
>>> isinstance(obj, list)
True
>>> isinstance(obj, tuple)
False
Python 的布林值新增的主要目標是使程式碼更清晰。例如,如果您正在閱讀一個函式並遇到語句 return 1
,您可能會想 1
是否表示布林真值、索引或某個其他數量的乘數係數。但是,如果語句是 return True
,則返回值的含義非常清晰。
Python 的布林值並不是為了嚴格的型別檢查而新增的。像 Pascal 這樣非常嚴格的語言也會阻止您對布林值執行算術運算,並且會要求 if
語句中的表示式始終評估為布林結果。Python 不會如此嚴格,也永遠不會如此,正如 PEP 285 明確指出的那樣。這意味著您仍然可以在 if
語句中使用任何表示式,即使是那些評估為列表、元組或某些隨機物件的表示式。布林型別是 int
類的子類,因此使用布林值進行算術運算仍然有效。
>>> True + 1
2
>>> False + 1
1
>>> False * 75
0
>>> True * 75
75
一句話總結 True
和 False
:它們是整數值 1 和 0 的替代拼寫方式,唯一的區別是 str()
和 repr()
返回字串 'True'
和 'False'
而不是 '1'
和 '0'
。
參見
- PEP 285 - 新增 bool 型別
由 GvR 撰寫並實現。
PEP 293:編解碼器錯誤處理回撥¶
將 Unicode 字串編碼為位元組字串時,可能會遇到無法編碼的字元。到目前為止,Python 允許將錯誤處理指定為“strict”(引發 UnicodeError
)、“ignore”(跳過字元)或“replace”(在輸出字串中使用問號),其中“strict”是預設行為。可能需要指定此類錯誤的其他處理方式,例如在轉換後的字串中插入 XML 字元引用或 HTML 實體引用。
Python 現在擁有一個靈活的框架來新增不同的處理策略。可以使用 codecs.register_error()
新增新的錯誤處理程式,然後編解碼器可以使用 codecs.lookup_error()
訪問錯誤處理程式。C 語言編寫的編解碼器也添加了等效的 C API。錯誤處理程式獲取必要的狀態資訊,例如正在轉換的字串、字串中檢測到錯誤的位置以及目標編碼。然後處理程式可以引發異常或返回替換字串。
使用此框架實現了兩個額外的錯誤處理程式:“backslashreplace”使用 Python 反斜槓引用來表示無法編碼的字元,“xmlcharrefreplace”發出 XML 字元引用。
參見
- PEP 293 - 編解碼器錯誤處理回撥
由 Walter Dörwald 撰寫並實現。
PEP 301:Distutils 的包索引和元資料¶
期待已久的 Python 目錄支援首次出現在 2.3 中。
目錄的核心是新的 Distutils register 命令。執行 python setup.py register
將收集描述包的元資料,例如其名稱、版本、維護者、描述等,並將其傳送到中央目錄伺服器。生成的目錄可從 https://pypi.org 獲得。
為了使目錄更有用,已向 Distutils setup()
函式添加了一個新的可選 *classifiers* 關鍵字引數。可以提供一個 Trove 樣式字串列表以幫助對軟體進行分類。
這是一個帶有分類器的 setup.py
示例,旨在與舊版本的 Distutils 相容
from distutils import core
kw = {'name': "Quixote",
'version': "0.5.1",
'description': "A highly Pythonic Web application framework",
# ...
}
if (hasattr(core, 'setup_keywords') and
'classifiers' in core.setup_keywords):
kw['classifiers'] = \
['Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Environment :: No Input/Output (Daemon)',
'Intended Audience :: Developers'],
core.setup(**kw)
可以透過執行 python setup.py register --list-classifiers
獲取分類器的完整列表。
參見
- PEP 301 - Distutils 的包索引和元資料
由 Richard Jones 撰寫並實現。
PEP 302:新的匯入鉤子¶
雖然自 Python 1.3 引入 ihooks
模組以來,就可以編寫自定義匯入鉤子,但沒有人真正滿意,因為編寫新的匯入鉤子既困難又混亂。曾有各種替代方案被提出,例如 imputil
和 iu
模組,但它們都沒有得到廣泛接受,而且它們都無法輕鬆地從 C 程式碼中使用。
PEP 302 借鑑了其前輩的思路,特別是 Gordon McMillan 的 iu
模組。sys
模組新增了三個條目
sys.path_hooks
是一個可呼叫物件列表;它們通常是類。每個可呼叫物件接受一個包含路徑的字串,並返回一個將處理此路徑的匯入器物件,或者在無法處理此路徑時引發ImportError
異常。sys.path_importer_cache
快取每個路徑的匯入器物件,因此sys.path_hooks
對於每個路徑只需遍歷一次。sys.meta_path
是一個匯入器物件列表,將在檢查sys.path
之前遍歷。此列表最初為空,但使用者程式碼可以向其中新增物件。透過新增到此列表中的物件可以匯入其他內建模組和凍結模組。
匯入器物件必須具有一個方法,即 find_module(fullname, path=None)
。*fullname* 將是模組或包名稱,例如 string
或 distutils.core
。find_module()
必須返回一個載入器物件,該載入器物件具有一個方法 load_module(fullname)
,用於建立並返回相應的模組物件。
因此,Python 新匯入邏輯的虛擬碼看起來像這樣(稍作簡化;有關完整詳細資訊,請參閱 PEP 302)
for mp in sys.meta_path:
loader = mp(fullname)
if loader is not None:
<module> = loader.load_module(fullname)
for path in sys.path:
for hook in sys.path_hooks:
try:
importer = hook(path)
except ImportError:
# ImportError, so try the other path hooks
pass
else:
loader = importer.find_module(fullname)
<module> = loader.load_module(fullname)
# Not found!
raise ImportError
參見
- PEP 302 - 新匯入鉤子
由 Just van Rossum 和 Paul Moore 撰寫。由 Just van Rossum 實現。
PEP 305:逗號分隔檔案¶
逗號分隔檔案是一種常用於從資料庫和電子表格匯出資料的格式。Python 2.3 添加了一個逗號分隔檔案解析器。
逗號分隔格式乍一看簡單得驚人
Costs,150,200,3.95
讀取一行並呼叫 line.split(',')
:還有什麼比這更簡單的呢?但如果字串資料中包含逗號,事情就會變得複雜
"Costs",150,200,3.95,"Includes taxes, shipping, and sundry items"
一個醜陋的大正則表示式可以解析它,但使用新的 csv
包要簡單得多
import csv
input = open('datafile', 'rb')
reader = csv.reader(input)
for line in reader:
print line
reader()
函式接受許多不同的選項。欄位分隔符不限於逗號,可以更改為任何字元,引用和行尾字元也可以。
可以定義和註冊逗號分隔檔案的不同方言;目前有兩種方言,都由 Microsoft Excel 使用。一個單獨的 csv.writer
類將從連續的元組或列表中生成逗號分隔檔案,並引用包含分隔符的字串。
參見
- PEP 305 - CSV 檔案 API
由 Kevin Altis、Dave Cole、Andrew McNamara、Skip Montanaro、Cliff Wells 撰寫並實現。
PEP 307:pickle 增強功能¶
pickle
和 cPickle
模組在 2.3 開發週期中受到了一些關注。在 2.2 中,新式類可以輕鬆地進行 pickle,但它們 pickle 得不夠緊湊;PEP 307 引用了一個簡單的示例,其中新式類導致 pickle 字串的長度是經典類的三倍。
解決方案是發明一種新的 pickle 協議。pickle.dumps()
函式長期以來一直支援文字或二進位制標誌。在 2.3 中,此標誌從布林值重新定義為整數:0 是舊的文字模式 pickle 格式,1 是舊的二進位制格式,現在 2 是新的 2.3 特有格式。一個新的常量 pickle.HIGHEST_PROTOCOL
可以用來選擇可用的最複雜的協議。
解包不再被認為是安全操作。2.2 的 pickle
提供了嘗試阻止不安全類被解包的鉤子(特別是 __safe_for_unpickling__
屬性),但這些程式碼從未經過審計,因此它們在 2.3 中都被刪除了。您不應在任何版本的 Python 中解包不受信任的資料。
為了減少新式類的 pickle 開銷,透過使用三個特殊方法添加了一個新的自定義 pickle 介面:__getstate__()
、__setstate__()
和 __getnewargs__()
。有關這些方法的完整語義,請查閱 PEP 307。
作為進一步壓縮 pickle 的一種方法,現在可以使用整數程式碼而不是長字串來標識 pickle 類。Python 軟體基金會將維護一個標準化程式碼列表;還有一個用於私人使用的程式碼範圍。目前尚未指定任何程式碼。
參見
- PEP 307 - pickle 協議的擴充套件
由 Guido van Rossum 和 Tim Peters 撰寫並實現。
擴充套件切片¶
自 Python 1.4 以來,切片語法就支援可選的第三個“步長”或“跨度”引數。例如,這些都是合法的 Python 語法:L[1:10:2]
、L[:-1:1]
、L[::-1]
。這是應 Numerical Python 開發人員的要求新增到 Python 中的,它廣泛使用了第三個引數。然而,Python 的內建列表、元組和字串序列型別從未支援此功能,如果您嘗試使用它,會引發 TypeError
。Michael Hudson 貢獻了一個補丁來修復這個缺點。
例如,您現在可以輕鬆地提取具有偶數索引的列表元素
>>> L = range(10)
>>> L[::2]
[0, 2, 4, 6, 8]
負值也有效,以相反的順序複製相同的列表
>>> L[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
這對於元組、陣列和字串也有效
>>> s='abcd'
>>> s[::2]
'ac'
>>> s[::-1]
'dcba'
如果您有一個可變序列,例如列表或陣列,您可以對擴充套件切片進行賦值或刪除,但是擴充套件切片和常規切片的賦值之間存在一些差異。對常規切片賦值可以用來改變序列的長度
>>> a = range(3)
>>> a
[0, 1, 2]
>>> a[1:3] = [4, 5, 6]
>>> a
[0, 4, 5, 6]
擴充套件切片沒有那麼靈活。當賦值給擴充套件切片時,語句右側的列表必須包含與它替換的切片相同數量的項
>>> a = range(4)
>>> a
[0, 1, 2, 3]
>>> a[::2]
[0, 2]
>>> a[::2] = [0, -1]
>>> a
[0, 1, -1, 3]
>>> a[::2] = [0,1,2]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: attempt to assign sequence of size 3 to extended slice of size 2
刪除更直接
>>> a = range(4)
>>> a
[0, 1, 2, 3]
>>> a[::2]
[0, 2]
>>> del a[::2]
>>> a
[1, 3]
現在也可以將切片物件傳遞給內建序列的 __getitem__()
方法
>>> range(10).__getitem__(slice(0, 5, 2))
[0, 2, 4]
或直接在下標中使用切片物件
>>> range(10)[slice(0, 5, 2)]
[0, 2, 4]
為了簡化支援擴充套件切片的序列的實現,切片物件現在有一個方法 indices(length)
,它給定序列的長度,返回一個 (start, stop, step)
元組,可以直接傳遞給 range()
。indices()
以與常規切片一致的方式處理省略和越界索引(這個看似無害的短語隱藏了許多令人困惑的細節!)。該方法旨在這樣使用
class FakeSeq:
...
def calc_item(self, i):
...
def __getitem__(self, item):
if isinstance(item, slice):
indices = item.indices(len(self))
return FakeSeq([self.calc_item(i) for i in range(*indices)])
else:
return self.calc_item(i)
從這個例子你還可以看出,內建的 slice
物件現在是切片型別的型別物件,不再是一個函式。這與 Python 2.2 保持一致,其中 int
、str
等也經歷了相同的變化。
其他語言更改¶
以下是 Python 2.3 對核心 Python 語言進行的所有更改。
yield
語句現在始終是關鍵字,如本文件的 PEP 255: Simple Generators 一節所述。添加了一個新的內建函式
enumerate()
,如本文件的 PEP 279: enumerate() 一節所述。新增了兩個常量,
True
和False
,以及內建的bool
型別,如本文件的 PEP 285: A Boolean Type 一節所述。int()
型別建構函式現在將返回一個長整數,而不是在字串或浮點數太大而無法適應整數時引發OverflowError
。這可能導致isinstance(int(expression), int)
為假的自相矛盾的結果,但這在實踐中似乎不太可能引起問題。內建型別現在支援擴充套件切片語法,如本文件 擴充套件切片 一節所述。
新增了一個內建函式
sum(iterable, start=0)
,它將可迭代物件中的數字項相加並返回它們的總和。sum()
只接受數字,這意味著您不能用它來連線一堆字串。(由 Alex Martelli 貢獻。)list.insert(pos, value)
過去在 *pos* 為負數時將 *value* 插入到列表的開頭。現在行為已更改為與切片索引一致,因此當 *pos* 為 -1 時,值將插入到倒數第二個元素之前,依此類推。list.index(value)
,它在列表中搜索 *value* 並返回其索引,現在接受可選的 *start* 和 *stop* 引數,以將搜尋限制在列表的一部分。字典有一個新方法
pop(key[, *default*])
,它返回與 *key* 對應的值,並從字典中刪除該鍵/值對。如果請求的鍵不在字典中,則如果指定了 *default* 則返回 *default*,否則引發KeyError
。>>> d = {1:2} >>> d {1: 2} >>> d.pop(4) Traceback (most recent call last): File "stdin", line 1, in ? KeyError: 4 >>> d.pop(1) 2 >>> d.pop(1) Traceback (most recent call last): File "stdin", line 1, in ? KeyError: 'pop(): dictionary is empty' >>> d {} >>>
還有一個新的類方法
dict.fromkeys(iterable, value)
,它建立一個字典,其鍵取自提供的迭代器 *iterable*,所有值都設定為 *value*,預設為None
。(補丁由 Raymond Hettinger 貢獻。)
此外,
dict()
建構函式現在接受關鍵字引數,以簡化建立小型字典>>> dict(red=1, blue=2, green=3, black=4) {'blue': 2, 'black': 4, 'green': 3, 'red': 1}
(由 Just van Rossum 貢獻。)
assert
語句不再檢查__debug__
標誌,因此您無法再透過賦值給__debug__
來停用斷言。使用-O
開關執行 Python 仍然會生成不執行任何斷言的程式碼。大多數型別物件現在都是可呼叫的,因此您可以使用它們建立新物件,例如函式、類和模組。(這意味著
new
模組在未來的 Python 版本中可能會被棄用,因為您現在可以使用types
模組中可用的型別物件。)例如,您可以使用以下程式碼建立新的模組物件>>> import types >>> m = types.ModuleType('abc','docstring') >>> m <module 'abc' (built-in)> >>> m.__doc__ 'docstring'
添加了一個新警告
PendingDeprecationWarning
,用於指示正在棄用的功能。預設情況下,該警告將不會列印。要檢查未來將棄用的功能的使用情況,請在命令列中提供-Walways::PendingDeprecationWarning::
或使用warnings.filterwarnings()
。基於字串的異常(例如
raise "Error occurred"
)的棄用過程已經開始。現在引發字串將觸發PendingDeprecationWarning
。將
None
用作變數名現在將導致SyntaxWarning
警告。在未來的 Python 版本中,None
最終可能會成為一個關鍵字。Python 2.1 中引入的檔案物件的
xreadlines()
方法不再必要,因為檔案現在可以作為自己的迭代器。xreadlines()
最初是為了更快地遍歷檔案中的所有行而引入的,但現在您可以簡單地編寫for line in file_obj
。檔案物件還有一個新的只讀encoding
屬性,它給出檔案使用的編碼;寫入檔案的 Unicode 字串將使用給定編碼自動轉換為位元組。新式類使用的方法解析順序已更改,儘管您只有在擁有真正複雜的繼承層次結構時才會注意到差異。經典類不受此更改的影響。Python 2.2 最初使用類的祖先的拓撲排序,但 2.3 現在使用論文 “A Monotonic Superclass Linearization for Dylan” 中描述的 C3 演算法。要了解此更改的動機,請閱讀 Michele Simionato 的文章 Python 2.3 方法解析順序,或閱讀 python-dev 上的執行緒,從 https://mail.python.org/pipermail/python-dev/2002-October/029035.html 的訊息開始。Samuele Pedroni 首先指出了問題並透過編寫 C3 演算法實現了修復。
Python 透過在執行 N 個位元組碼後切換執行緒來執行多執行緒程式。N 的預設值已從 10 增加到 100 個位元組碼,透過減少切換開銷來加速單執行緒應用程式。一些多執行緒應用程式可能會遇到較慢的響應時間,但這很容易透過使用
sys.setcheckinterval(N)
將限制設定回較低的數字來解決。可以使用新的sys.getcheckinterval()
函式檢索該限制。一個微小但影響深遠的改變是,Python 隨附模組定義的擴充套件型別的名稱現在包含模組名和型別名前的
'.'
。例如,在 Python 2.2 中,如果你建立一個套接字並列印它的__class__
,你會得到以下輸出>>> s = socket.socket() >>> s.__class__ <type 'socket'>
在 2.3 中,你得到這個
>>> s.__class__ <type '_socket.socket'>
舊式和新式類之間的一個顯著不相容性已被移除:您現在可以為新式類的
__name__
和__bases__
屬性賦值。對__bases__
的賦值有一些限制,類似於與例項的__class__
屬性相關的限制。
字串更改¶
in
運算子現在對字串有不同的工作方式。以前,當評估X in Y
(其中 *X* 和 *Y* 是字串)時,*X* 只能是單個字元。現在已更改;*X* 可以是任意長度的字串,並且如果 *X* 是 *Y* 的子字串,X in Y
將返回True
。如果 *X* 是空字串,結果總是True
。>>> 'ab' in 'abcd' True >>> 'ad' in 'abcd' False >>> '' in 'abcd' True
請注意,這不會告訴您子字串從何處開始;如果您需要該資訊,請使用
find()
字串方法。strip()
、lstrip()
和rstrip()
字串方法現在有一個可選引數,用於指定要剝離的字元。預設仍然是刪除所有空白字元>>> ' abc '.strip() 'abc' >>> '><><abc<><><>'.strip('<>') 'abc' >>> '><><abc<><><>\n'.strip('<>') 'abc<><><>\n' >>> u'\u4000\u4001abc\u4000'.strip(u'\u4000') u'\u4001abc' >>>
(由 Simon Brunning 建議並由 Walter Dörwald 實現。)
startswith()
和endswith()
字串方法現在接受負數作為 *start* 和 *end* 引數。另一個新的字串方法是
zfill()
,它最初是string
模組中的一個函式。zfill()
用左側的零填充數字字串,直到達到指定的寬度。請注意,%
運算子仍然比zfill()
更靈活和強大。>>> '45'.zfill(4) '0045' >>> '12345'.zfill(4) '12345' >>> 'goofy'.zfill(6) '0goofy'
(由 Walter Dörwald 貢獻。)
添加了一個新型別物件
basestring
。8 位字串和 Unicode 字串都繼承自此型別,因此isinstance(obj, basestring)
對於這兩種字串都將返回True
。它是一個完全抽象的型別,因此您無法建立basestring
例項。內部化字串不再是永恆的,當它們唯一的引用來自內部化字串的內部字典時,它們現在會以通常的方式進行垃圾回收。(由 Oren Tirosh 實現。)
最佳化¶
新式類例項的建立速度得到了大幅提升;現在它們比經典類更快!
列表物件的
sort()
方法已被 Tim Peters 大量重寫,實現速度顯著提高。由於卡拉楚巴乘法(Karatsuba multiplication)的實現,大長整數的乘法現在快得多,這是一種比學校乘法演算法所需的 *O*(n2) 更好地擴充套件的演算法。(原始補丁由 Christopher A. Craig 貢獻,並由 Tim Peters 大量修改。)
SET_LINENO
操作碼現在已消失。這可能會提供一個小小的速度提升,具體取決於您的編譯器的特性。有關更長的解釋,請參閱 其他更改和修復 一節。(由 Michael Hudson 移除。)xrange()
物件現在有自己的迭代器,使得for i in xrange(n)
略快於for i in range(n)
。(由 Raymond Hettinger 貢獻的補丁。)在各種熱點中進行了一些小的重新排列以提高效能,例如行內函數或刪除一些程式碼。(主要由 GvR 實現,但許多人都貢獻了單次更改。)
2.3 最佳化結果是 Python 2.3 執行 pystone 基準測試比 Python 2.2 快約 25%。
新的、改進的和已棄用的模組¶
一如既往,Python 的標準庫獲得了一些增強和錯誤修復。以下是按模組名稱字母順序排列的最顯著更改的部分列表。有關更完整的更改列表,請查閱原始碼樹中的 Misc/NEWS
檔案,或檢視 CVS 日誌以獲取所有詳細資訊。
array
模組現在使用'u'
格式字元支援 Unicode 字元陣列。陣列現在還支援使用+=
賦值運算子新增另一個數組的內容,以及使用*=
賦值運算子重複陣列。(由 Jason Orendorff 貢獻。)bsddb
模組已被 PyBSDDB 包的 4.1.6 版替換,它為 BerkeleyDB 庫的事務功能提供了更完整的介面。舊版本的模組已重新命名為
bsddb185
,不再自動構建;您需要編輯Modules/Setup
以啟用它。請注意,新的bsddb
包旨在與舊模組相容,因此如果發現任何不相容之處,請務必提交錯誤報告。將 Python 升級到 2.3 時,如果新的直譯器是使用底層 BerkeleyDB 庫的新版本編譯的,您幾乎肯定需要將資料庫檔案轉換為新版本。您可以使用新的指令碼db2pickle.py
和pickle2db.py
相當輕鬆地完成此操作,這些指令碼位於發行版的Tools/scripts
目錄中。如果您已經在使用 PyBSDDB 包並將其匯入為bsddb3
,則必須更改您的import
語句,將其匯入為bsddb
。新的
bz2
模組是 bz2 資料壓縮庫的介面。bz2 壓縮資料通常比相應的zlib
壓縮資料更小。(由 Gustavo Niemeyer 貢獻。)新的
datetime
模組中添加了一組標準日期/時間型別。有關更多詳細資訊,請參閱下一節。Distutils
Extension
類現在支援一個名為 depends 的額外建構函式引數,用於列出擴充套件所依賴的額外原始檔。這允許 Distutils 在任何依賴檔案被修改時重新編譯模組。例如,如果sampmodule.c
包含標頭檔案sample.h
,您將像這樣建立Extension
物件ext = Extension("samp", sources=["sampmodule.c"], depends=["sample.h"])
修改
sample.h
將導致模組被重新編譯。(由 Jeremy Hylton 貢獻。)Distutils 的其他次要更改:它現在檢查
CC
、CFLAGS
、CPP
、LDFLAGS
和CPPFLAGS
環境變數,並使用它們覆蓋 Python 配置中的設定(由 Robert Weber 貢獻)。以前,
doctest
模組只會搜尋公共方法和函式的文件字串中的測試用例,但現在它也檢查私有方法和函式。DocTestSuite()
函式從一組doctest
測試中建立一個unittest.TestSuite
物件。新的
gc.get_referents(object)
函式返回 object 引用所有物件的列表。getopt
模組新增了一個函式gnu_getopt()
,它支援與現有getopt()
函式相同的引數,但使用 GNU 風格的掃描模式。現有的getopt()
在遇到非選項引數時會停止處理選項,但在 GNU 風格模式下處理會繼續,這意味著選項和引數可以混合使用。例如>>> getopt.getopt(['-f', 'filename', 'output', '-v'], 'f:v') ([('-f', 'filename')], ['output', '-v']) >>> getopt.gnu_getopt(['-f', 'filename', 'output', '-v'], 'f:v') ([('-f', 'filename'), ('-v', '')], ['output'])
(由 Peter Åstrand 貢獻。)
grp
、pwd
和resource
模組現在返回增強的元組>>> import grp >>> g = grp.getgrnam('amk') >>> g.gr_name, g.gr_gid ('amk', 500)
gzip
模組現在可以處理超過 2 GiB 的檔案。新的
heapq
模組包含堆佇列演算法的實現。堆是一種類似陣列的資料結構,它以部分排序的方式儲存項,使得對於每個索引 k,heap[k] <= heap[2*k+1]
和heap[k] <= heap[2*k+2]
。這使得刪除最小項變得很快,並且在維護堆屬性的同時插入新項的時間複雜度為 O(log n)。(有關優先順序佇列資料結構的更多資訊,請參閱 https://xlinux.nist.gov/dads//HTML/priorityque.html。)heapq
模組提供了heappush()
和heappop()
函式,用於在其他可變 Python 序列型別之上新增和刪除項,同時保持堆屬性。以下是一個使用 Python 列表的示例>>> import heapq >>> heap = [] >>> for item in [3, 7, 5, 11, 1]: ... heapq.heappush(heap, item) ... >>> heap [1, 3, 5, 11, 7] >>> heapq.heappop(heap) 1 >>> heapq.heappop(heap) 3 >>> heap [5, 7, 11]
(由 Kevin O’Connor 貢獻。)
IDLE 整合開發環境已使用 IDLEfork 專案的程式碼(https://idlefork.sourceforge.net)進行了更新。最值得注意的特性是,正在開發的程式碼現在在子程序中執行,這意味著不再需要手動
reload()
操作。IDLE 的核心程式碼已作為idlelib
包合併到標準庫中。imaplib
模組現在支援基於 SSL 的 IMAP。(由 Piers Lauder 和 Tino Lange 貢獻。)itertools
包含許多與迭代器一起使用的有用函式,其靈感來自 ML 和 Haskell 語言提供的各種函式。例如,itertools.ifilter(predicate, iterator)
返回迭代器中函式predicate()
返回True
的所有元素,而itertools.repeat(obj, N)
返回obj
N 次。模組中還有許多其他函式;有關詳細資訊,請參閱包的參考文件。(由 Raymond Hettinger 貢獻。)math
模組中的兩個新函式degrees(rads)
和radians(degs)
用於在弧度制和角度制之間進行轉換。math
模組中的其他函式,例如math.sin()
和math.cos()
,始終需要以弧度測量的輸入值。此外,math.log()
中添加了一個可選的 base 引數,以便更容易計算非e
和10
為底的對數。(由 Raymond Hettinger 貢獻。)posix
模組中添加了幾個新的 POSIX 函式(getpgid()
、killpg()
、lchown()
、loadavg()
、major()
、makedev()
、minor()
和mknod()
),該模組是os
模組的基礎。(由 Gustavo Niemeyer、Geert Jansen 和 Denis S. Otkidach 貢獻。)在
os
模組中,*stat()
系列函式現在可以報告時間戳中的秒分數。此類時間戳表示為浮點數,類似於time.time()
返回的值。在測試期間,發現如果時間戳是浮點數,某些應用程式會中斷。為了相容性,當使用
stat_result
的元組介面時,時間戳將表示為整數。當使用命名欄位(Python 2.2 中首次引入的功能)時,時間戳仍然表示為整數,除非呼叫os.stat_float_times()
以啟用浮點返回值>>> os.stat("/tmp").st_mtime 1034791200 >>> os.stat_float_times(True) >>> os.stat("/tmp").st_mtime 1034791200.6335014
在 Python 2.4 中,預設將更改為始終返回浮點數。
應用程式開發人員應僅在所有庫在遇到浮點時間戳時都能正常工作,或者如果他們使用元組 API 時才啟用此功能。如果使用此功能,則應在應用程式級別啟用此功能,而不是嘗試按每次使用的方式啟用它。
optparse
模組包含一個新的命令列引數解析器,可以將選項值轉換為特定的 Python 型別,並自動生成用法訊息。有關更多詳細資訊,請參閱下一節。舊的且從未文件化的
linuxaudiodev
模組已被棄用,並添加了一個名為ossaudiodev
的新版本。該模組之所以重新命名,是因為 OSS 聲音驅動程式可以在 Linux 以外的平臺上使用,並且介面也以各種方式進行了整理和更新。(由 Greg Ward 和 Nicholas FitzRoy-Dale 貢獻。)新的
platform
模組包含許多函式,這些函式嘗試確定您正在執行的平臺的各種屬性。有獲取架構、CPU 型別、Windows 作業系統版本甚至 Linux 發行版版本的函式。(由 Marc-André Lemburg 貢獻。)pyexpat
模組提供的解析器物件現在可以選擇緩衝字元資料,從而減少對字元資料處理程式的呼叫次數,從而提高效能。將解析器物件的buffer_text
屬性設定為True
將啟用緩衝。sample(population, k)
函式已新增到random
模組中。population 是一個序列或xrange
物件,包含總體中的元素,而sample()
從總體中選擇 k 個元素,而不替換已選擇的元素。k 可以是任何值,最大可達len(population)
。例如>>> days = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'St', 'Sn'] >>> random.sample(days, 3) # Choose 3 elements ['St', 'Sn', 'Th'] >>> random.sample(days, 7) # Choose 7 elements ['Tu', 'Th', 'Mo', 'We', 'St', 'Fr', 'Sn'] >>> random.sample(days, 7) # Choose 7 again ['We', 'Mo', 'Sn', 'Fr', 'Tu', 'St', 'Th'] >>> random.sample(days, 8) # Can't choose eight Traceback (most recent call last): File "<stdin>", line 1, in ? File "random.py", line 414, in sample raise ValueError, "sample larger than population" ValueError: sample larger than population >>> random.sample(xrange(1,10000,2), 10) # Choose ten odd nos. under 10000 [3407, 3805, 1505, 7023, 2401, 2267, 9733, 3151, 8083, 9195]
random
模組現在使用 Mersenne Twister 演算法(用 C 實現)。它比以前的演算法更快,並且經過了更廣泛的研究。(所有更改均由 Raymond Hettinger 貢獻。)
readline
模組還新增了幾個函式:get_history_item()
、get_current_history_length()
和redisplay()
。rexec
和Bastion
模組已被宣佈廢棄,嘗試匯入它們將因RuntimeError
而失敗。新式類提供了新的方法來突破rexec
提供的受限執行環境,並且沒有人有興趣或時間來修復它們。如果您的應用程式使用rexec
,請將其重寫以使用其他東西。(堅持使用 Python 2.2 或 2.1 不會使您的應用程式更安全,因為這些版本中的
rexec
模組存在已知錯誤。重申:如果您正在使用rexec
,請立即停止使用它。)rotor
模組已被棄用,因為其用於加密的演算法被認為不安全。如果需要加密,請使用單獨提供的幾個 AES Python 模組之一。shutil
模組新增了一個move(src, dest)
函式,該函式遞迴地將檔案或目錄移動到新位置。對更高階的 POSIX 訊號處理的支援已新增到
signal
中,但後來又被刪除,因為它被證明無法在不同平臺上可靠地工作。socket
模組現在支援超時。您可以在 socket 物件上呼叫settimeout(t)
方法來設定 t 秒的超時。後續的 socket 操作如果完成時間超過 t 秒,將中止並引發socket.timeout
異常。最初的超時實現由 Tim O’Malley 完成。Michael Gilfix 將其整合到 Python
socket
模組中,並經過了漫長的審查。程式碼提交後,Guido van Rossum 對其部分進行了重寫。(這是一個協作開發過程的良好示例。)在 Windows 上,
socket
模組現在隨附 Secure Sockets Layer (SSL) 支援。C
PYTHON_API_VERSION
宏的值現在在 Python 級別公開為sys.api_version
。可以透過呼叫新的sys.exc_clear()
函式來清除當前異常。新的
tarfile
模組允許讀取和寫入 tar 格式的歸檔檔案。(由 Lars Gustäbel 貢獻。)新的
textwrap
模組包含用於包裝包含文字段落的字串的函式。wrap(text, width)
函式接受一個字串並返回一個列表,其中包含按不超過所選寬度拆分成行的文字。fill(text, width)
函式返回一個重新格式化為不超過所選寬度的行的單個字串。(正如您所猜測的,fill()
是基於wrap()
構建的。例如>>> import textwrap >>> paragraph = "Not a whit, we defy augury: ... more text ..." >>> textwrap.wrap(paragraph, 60) ["Not a whit, we defy augury: there's a special providence in", "the fall of a sparrow. If it be now, 'tis not to come; if it", ...] >>> print textwrap.fill(paragraph, 35) Not a whit, we defy augury: there's a special providence in the fall of a sparrow. If it be now, 'tis not to come; if it be not to come, it will be now; if it be not now, yet it will come: the readiness is all. >>>
該模組還包含一個
TextWrapper
類,該類實際實現了文字換行策略。TextWrapper
類以及wrap()
和fill()
函式都支援許多額外的關鍵字引數,用於微調格式;有關詳細資訊,請查閱模組的文件。(由 Greg Ward 貢獻。)thread
和threading
模組現在有了配套模組dummy_thread
和dummy_threading
,它們為不支援執行緒的平臺提供了thread
模組介面的無操作實現。目的是透過在頂部放置以下程式碼來簡化支援執行緒的模組(那些*不*依賴執行緒執行的模組)try: import threading as _threading except ImportError: import dummy_threading as _threading
在此示例中,
_threading
用作模組名稱,以明確表示所使用的模組不一定是實際的threading
模組。無論是否支援執行緒,程式碼都可以呼叫_threading
中的函式和使用其中的類,從而避免使用if
語句並使程式碼更清晰。此模組不會神奇地使多執行緒程式碼在沒有執行緒的情況下執行;等待另一個執行緒返回或執行某事物的程式碼將簡單地永遠掛起。time
模組的strptime()
函式長期以來一直令人煩惱,因為它使用平臺 C 庫的strptime()
實現,而不同的平臺有時會出現奇怪的錯誤。Brett Cannon 貢獻了一個用純 Python 編寫的可移植實現,它應該在所有平臺上表現一致。新的
timeit
模組有助於測量 Python 程式碼片段的執行時間。timeit.py
檔案可以直接從命令列執行,或者模組的Timer
類可以直接匯入和使用。這是一個簡短的示例,用於判斷是將 8 位字串轉換為 Unicode 更快,是透過在其後附加一個空 Unicode 字串,還是透過使用unicode()
函式import timeit timer1 = timeit.Timer('unicode("abc")') timer2 = timeit.Timer('"abc" + u""') # Run three trials print timer1.repeat(repeat=3, number=100000) print timer2.repeat(repeat=3, number=100000) # On my laptop this outputs: # [0.36831796169281006, 0.37441694736480713, 0.35304892063140869] # [0.17574405670166016, 0.18193507194519043, 0.17565798759460449]
Tix
模組已針對 Tix 包的當前版本進行了各種錯誤修復和更新。Tkinter
模組現在與支援執行緒的 Tcl 版本一起工作。Tcl 的執行緒模型要求只能從建立 widget 的執行緒訪問 widget;從另一個執行緒訪問可能導致 Tcl 崩潰。對於某些 Tcl 介面,Tkinter
現在將在從不同執行緒訪問 widget 時,透過編組命令、將其傳遞給正確的執行緒並等待結果來自動避免此問題。其他介面無法自動處理,但Tkinter
現在將在此類訪問時引發異常,以便您至少可以發現問題。有關此更改的更詳細解釋,請參閱 https://mail.python.org/pipermail/python-dev/2002-December/031107.html。(由 Martin von Löwis 實現。)透過
_tkinter
呼叫 Tcl 方法不再只返回字串。相反,如果 Tcl 返回其他物件,這些物件將被轉換為其 Python 等效項(如果存在),或者如果沒有 Python 等效項,則用_tkinter.Tcl_Obj
物件包裝。當透過
Tkinter
模組使用_tkinter
時(大多數 Tkinter 應用程式都會如此),此功能始終處於啟用狀態。它不應導致相容性問題,因為 Tkinter 始終會在可能的情況下將字串結果轉換為 Python 型別。如果發現任何不相容性,可以透過在建立第一個
tkapp
物件之前,將Tkinter
模組中的wantobjects
變數設定為 false 來恢復舊行為。import Tkinter Tkinter.wantobjects = 0
由此更改引起的任何中斷都應報告為錯誤。
UserDict
模組有一個新的DictMixin
類,它為已經具有最小對映介面的類定義了所有字典方法。這大大簡化了編寫需要替換字典的類,例如shelve
模組中的類。只要類定義了
__getitem__()
、__setitem__()
、__delitem__()
和keys()
,將混入作為超類可提供完整的字典介面。例如>>> import UserDict >>> class SeqDict(UserDict.DictMixin): ... """Dictionary lookalike implemented with lists.""" ... def __init__(self): ... self.keylist = [] ... self.valuelist = [] ... def __getitem__(self, key): ... try: ... i = self.keylist.index(key) ... except ValueError: ... raise KeyError ... return self.valuelist[i] ... def __setitem__(self, key, value): ... try: ... i = self.keylist.index(key) ... self.valuelist[i] = value ... except ValueError: ... self.keylist.append(key) ... self.valuelist.append(value) ... def __delitem__(self, key): ... try: ... i = self.keylist.index(key) ... except ValueError: ... raise KeyError ... self.keylist.pop(i) ... self.valuelist.pop(i) ... def keys(self): ... return list(self.keylist) ... >>> s = SeqDict() >>> dir(s) # See that other dictionary methods are implemented ['__cmp__', '__contains__', '__delitem__', '__doc__', '__getitem__', '__init__', '__iter__', '__len__', '__module__', '__repr__', '__setitem__', 'clear', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keylist', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'valuelist', 'values']
(由 Raymond Hettinger 貢獻。)
xml.dom.minidom
中的 DOM 實現現在可以透過向 DOM 節點的toxml()
和toprettyxml()
方法提供可選的編碼引數來生成特定編碼的 XML 輸出。xmlrpclib
模組現在支援一個 XML-RPC 擴充套件,用於處理 nil 資料值,例如 Python 的None
。在解組 XML-RPC 響應時,始終支援 nil 值。要生成包含None
的請求,您必須在建立Marshaller
例項時為 allow_none 引數提供一個真值。新的
DocXMLRPCServer
模組允許編寫自文件化的 XML-RPC 伺服器。在演示模式下(作為程式)執行它以檢視其執行情況。將 Web 瀏覽器指向 RPC 伺服器會生成 pydoc 樣式的文件;將 xmlrpclib 指向伺服器允許呼叫實際方法。(由 Brian Quinlan 貢獻。)增加了對國際化域名(RFC 3454、3490、3491 和 3492)的支援。“idna”編碼可用於在 Unicode 域名和該名稱的 ASCII 相容編碼 (ACE) 之間進行轉換。
>{}>{}> u"www.Alliancefrançaise.nu".encode("idna") 'www.xn--alliancefranaise-npb.nu'
socket
模組也已擴充套件,以便在將 Unicode 主機名傳遞給 C 庫之前,將其透明地轉換為 ACE 版本。處理主機名的模組(例如httplib
和ftplib
)也支援 Unicode 主機名;httplib
還使用域名的 ACE 版本傳送 HTTPHost
頭。urllib
支援帶有非 ASCII 主機名的 Unicode URL,只要 URL 的path
部分僅為 ASCII。為了實現這一變化,新增了
stringprep
模組、mkstringprep
工具和punycode
編碼。
日期/時間型別¶
適合表示時間戳的日期和時間型別已作為 datetime
模組新增。這些型別不支援不同的日曆或許多花哨的功能,僅限於表示時間的基本功能。
三個主要型別是:date
,表示日、月和年;time
,由時、分和秒組成;以及 datetime
,它包含 date
和 time
的所有屬性。還有一個 timedelta
類,表示兩個時間點之間的差異,時區邏輯由繼承自抽象 tzinfo
類的類實現。
您可以建立 date
和 time
的例項,可以透過向相應的建構函式提供關鍵字引數,例如 datetime.date(year=1972, month=10, day=15)
,也可以使用許多類方法之一。例如,today()
類方法返回當前本地日期。
建立後,日期/時間類的例項都是不可變的。有許多方法可以從物件生成格式化字串
>>> import datetime
>>> now = datetime.datetime.now()
>>> now.isoformat()
'2002-12-30T21:27:03.994956'
>>> now.ctime() # Only available on date, datetime
'Mon Dec 30 21:27:03 2002'
>>> now.strftime('%Y %d %b')
'2002 30 Dec'
replace()
方法允許修改 date
或 datetime
例項的一個或多個欄位,返回一個新例項
>>> d = datetime.datetime.now()
>>> d
datetime.datetime(2002, 12, 30, 22, 15, 38, 827738)
>>> d.replace(year=2001, hour = 12)
datetime.datetime(2001, 12, 30, 12, 15, 38, 827738)
>>>
例項可以進行比較、雜湊和轉換為字串(結果與 isoformat()
的結果相同)。date
和 datetime
例項可以相互減去,並新增到 timedelta
例項。最大的缺失功能是沒有標準庫支援解析字串並返回 date
或 datetime
。
有關更多資訊,請參閱模組的參考文件。(由 Tim Peters 貢獻。)
optparse 模組¶
getopt
模組提供簡單的命令列引數解析。新的 optparse
模組(最初名為 Optik)提供更精細的命令列解析,它遵循 Unix 約定,自動建立 --help
的輸出,並可以對不同的選項執行不同的操作。
首先,您需要建立一個 OptionParser
例項,並告訴它您的程式有哪些選項。
import sys
from optparse import OptionParser
op = OptionParser()
op.add_option('-i', '--input',
action='store', type='string', dest='input',
help='set input filename')
op.add_option('-l', '--length',
action='store', type='int', dest='length',
help='set maximum length of output')
然後透過呼叫 parse_args()
方法來解析命令列。
options, args = op.parse_args(sys.argv[1:])
print options
print args
這會返回一個包含所有選項值的物件,以及一個包含其餘引數的字串列表。
現在,使用各種引數呼叫指令碼會按預期工作。請注意,length 引數會自動轉換為整數。
$ ./python opt.py -i data arg1
<Values at 0x400cad4c: {'input': 'data', 'length': None}>
['arg1']
$ ./python opt.py --input=data --length=4
<Values at 0x400cad2c: {'input': 'data', 'length': 4}>
[]
$
幫助訊息會自動為您生成
$ ./python opt.py --help
usage: opt.py [options]
options:
-h, --help show this help message and exit
-iINPUT, --input=INPUT
set input filename
-lLENGTH, --length=LENGTH
set maximum length of output
$
更多詳情請參閱模組文件。
Optik 由 Greg Ward 編寫,並得到了 Getopt SIG 讀者的建議。
Pymalloc:一個專用物件分配器¶
Pymalloc 是由 Vladimir Marangozov 編寫的專用物件分配器,是 Python 2.1 中新增的一項功能。Pymalloc 旨在比系統 malloc()
更快,並且對於 Python 程式典型的分配模式具有更少的記憶體開銷。分配器使用 C 的 malloc()
函式獲取大塊記憶體池,然後從這些記憶體池中滿足較小的記憶體請求。
在 2.1 和 2.2 中,pymalloc 是一個實驗性功能,預設情況下未啟用;您必須在編譯 Python 時透過向 configure 指令碼提供 --with-pymalloc
選項來顯式啟用它。在 2.3 中,pymalloc 得到了進一步增強,現在預設啟用;您必須提供 --without-pymalloc
來停用它。
此更改對於用 Python 編寫的程式碼是透明的;但是,pymalloc 可能會暴露 C 擴充套件中的錯誤。C 擴充套件模組的作者應在啟用 pymalloc 的情況下測試其程式碼,因為某些不正確的程式碼可能在執行時導致核心轉儲。
有一個特別常見的錯誤會導致問題。Python 的 C API 中有許多記憶體分配函式以前只是 C 庫的 malloc()
和 free()
的別名,這意味著如果您不小心呼叫了不匹配的函式,錯誤就不會被發現。當啟用物件分配器時,這些函式不再是 malloc()
和 free()
的別名,呼叫錯誤的函式來釋放記憶體可能會導致核心轉儲。例如,如果記憶體是使用 PyObject_Malloc()
分配的,則必須使用 PyObject_Free()
釋放,而不是 free()
。Python 中包含的一些模組就犯了此錯誤,不得不進行修復;毫無疑問,還有更多第三方模組會遇到同樣的問題。
作為此更改的一部分,令人困惑的多個記憶體分配介面已整合為兩個 API 系列。使用一個系列分配的記憶體不得使用另一個系列的函式進行操作。一個系列用於分配記憶體塊,另一個函式系列專門用於分配 Python 物件。
要分配和釋放不區分的記憶體塊,請使用“原始記憶體”系列:
PyMem_Malloc()
、PyMem_Realloc()
和PyMem_Free()
。“物件記憶體”系列是上述 pymalloc 功能的介面,偏向於大量“小”分配:
PyObject_Malloc()
、PyObject_Realloc()
和PyObject_Free()
。要分配和釋放 Python 物件,請使用“物件”系列
PyObject_New
、PyObject_NewVar
和PyObject_Del()
。
多虧了 Tim Peters 的大量工作,2.3 中的 pymalloc 還提供了除錯功能,可以在擴充套件模組和直譯器本身中捕獲記憶體覆蓋和雙重釋放。要啟用此支援,請透過執行 configure 並使用 --with-pydebug
來編譯 Python 直譯器的除錯版本。
為了幫助擴充套件編寫者,Python 2.3 原始碼隨附了一個頭檔案 Misc/pymemcompat.h
,允許 Python 擴充套件在使用 Python 1.5.2 以來的任何版本編譯時使用 2.3 介面進行記憶體分配。您可以從 Python 原始碼發行版中複製該檔案並將其與擴充套件的原始碼捆綁在一起。
參見
- https://hg.python.org/cpython/file/default/Objects/obmalloc.c
有關 pymalloc 實現的完整詳細資訊,請參閱 Python 原始碼中
Objects/obmalloc.c
檔案頂部的註釋。上述連結指向 python.org SVN 瀏覽器中的檔案。
構建和 C API 更改¶
Python 的構建過程和 C API 的更改包括
垃圾回收使用的迴圈檢測實現已被證明是穩定的,因此現在已強制執行。您無法再在沒有它的情況下編譯 Python,並且 configure 的
--with-cycle-gc
開關已被刪除。現在,透過在執行 Python 的 configure 指令碼時提供
--enable-shared
,Python 可以選擇構建為共享庫(libpython2.3.so
)。(由 Ondrej Palkovsky 貢獻。)DL_EXPORT
和DL_IMPORT
宏現在已棄用。Python 擴充套件模組的初始化函式現在應使用新的宏PyMODINIT_FUNC
宣告,而 Python 核心通常將使用PyAPI_FUNC
和PyAPI_DATA
宏。直譯器可以在不包含內建函式和模組的任何文件字串的情況下編譯,方法是向 configure 指令碼提供
--without-doc-strings
。這會使 Python 可執行檔案縮小約 10%,但也意味著您無法獲取 Python 內建函式的幫助。(由 Gustavo Niemeyer 貢獻。)PyArg_NoArgs()
宏現在已棄用,使用它的程式碼應進行更改。對於 Python 2.2 及更高版本,方法定義表可以指定METH_NOARGS
標誌,表示沒有引數,然後可以刪除引數檢查。如果與 2.2 之前的 Python 版本相容很重要,程式碼可以使用PyArg_ParseTuple(args, "")
代替,但這會比使用METH_NOARGS
慢。PyArg_ParseTuple()
接受用於各種大小的無符號整數的新格式字元:B
代表 unsigned char,H
代表 unsigned short int,I
代表 unsigned int,K
代表 unsigned long long。新增函式
PyObject_DelItemString(mapping, char *key)
作為PyObject_DelItem(mapping, PyString_New(key))
的簡寫。檔案物件現在以不同的方式管理其內部字串緩衝區,在需要時以指數方式增加。這導致
Lib/test/test_bufio.py
中的基準測試顯著加速(根據一次測量,從 57 秒縮短到 1.7 秒)。現在可以透過在方法的
PyMethodDef
結構中設定METH_CLASS
或METH_STATIC
標誌來為 C 擴充套件型別定義類方法和靜態方法。Python 現在包含了 Expat XML 解析器的原始碼副本,消除了對系統版本或本地 Expat 安裝的任何依賴。
如果您在擴充套件中動態分配型別物件,則應該注意與
__module__
和__name__
屬性相關的規則發生了變化。簡而言之,您將需要確保型別的字典包含'__module__'
鍵;將模組名稱作為型別名稱中最後一個句點之前的部分將不再產生預期的效果。有關更多詳細資訊,請閱讀 API 參考文件或原始碼。
特定於埠的更改¶
對 IBM OS/2 使用 EMX 執行時環境的移植支援已合併到主 Python 原始碼樹中。EMX 是 OS/2 系統 API 之上的 POSIX 模擬層。EMX 的 Python 移植嘗試支援 EMX 執行時公開的所有類似 POSIX 的功能,並且大部分成功;fork()
和 fcntl()
受底層模擬層的限制。標準 OS/2 移植(使用 IBM 的 Visual Age 編譯器)也獲得了對大小寫敏感匯入語義的支援,作為 EMX 移植整合到 CVS 的一部分。(由 Andrew MacIntyre 貢獻。)
在 MacOS 上,大多數工具箱模組已被弱連結以提高向後相容性。這意味著如果當前作業系統版本缺少單個例程,模組將不再無法載入。相反,呼叫缺失的例程將引發異常。(由 Jack Jansen 貢獻。)
RPM 規範檔案,位於 Python 原始碼發行版中的 Misc/RPM/
目錄中,已針對 2.3 進行了更新。(由 Sean Reifschneider 貢獻。)
Python 現在支援的其他新平臺包括 AtheOS(http://www.atheos.cx/)、GNU/Hurd 和 OpenVMS。
其他更改和修復¶
像往常一樣,整個原始碼樹中散佈著許多其他改進和錯誤修復。搜尋 CVS 更改日誌發現,在 Python 2.2 和 2.3 之間應用了 523 個補丁,修復了 514 個錯誤。這兩個數字可能都被低估了。
一些更顯著的變化是
如果設定了
PYTHONINSPECT
環境變數,Python 直譯器將在執行 Python 程式後進入互動式提示符,就像 Python 是使用-i
選項呼叫一樣。環境變數可以在執行 Python 直譯器之前設定,也可以由 Python 程式作為其執行的一部分設定。regrtest.py
指令碼現在提供了一種允許“所有資源,除了 foo”的方法。傳遞給-u
選項的資源名稱現在可以加上連字元('-'
)作為字首,表示“移除此資源”。例如,選項 '-uall,-bsddb
' 可以用來啟用所有資源,除了bsddb
。現在用於構建文件的工具可以在 Cygwin 和 Unix 下工作。
SET_LINENO
操作碼已被移除。在很久以前,這個操作碼需要用於在回溯中生成行號並支援跟蹤函式(例如,用於pdb
)。自 Python 1.5 以來,回溯中的行號已使用不同的機制計算,該機制適用於“python -O”。對於 Python 2.3,Michael Hudson 實現了一個類似的方案來確定何時呼叫跟蹤函式,完全消除了對SET_LINENO
的需求。除了 Python 在不帶
-O
執行時速度略有加快之外,很難從 Python 程式碼中檢測到任何由此產生的差異。訪問框架物件
f_lineno
欄位的 C 擴充套件應改為呼叫PyCode_Addr2Line(f->f_code, f->f_lasti)
。這將額外產生使程式碼在早期 Python 版本的“python -O”下按預期工作的效果。一個巧妙的新功能是跟蹤函式現在可以賦值給框架物件的
f_lineno
屬性,從而更改將要執行的下一行。pdb
偵錯程式已新增一個jump
命令,利用了此新功能。(由 Richie Hindle 實現。)
移植到 Python 2.3¶
本節列出了可能需要更改程式碼的先前描述的更改
yield
現在始終是一個關鍵字;如果它在您的程式碼中用作變數名,則必須選擇不同的名稱。對於字串 X 和 Y,如果 X 的長度超過一個字元,
X in Y
現在也能正常工作。int()
型別建構函式現在將返回一個長整數,而不是在字串或浮點數太大而無法容納在整數中時引發OverflowError
。如果您的 Unicode 字串包含 8 位字元,則必須透過在檔案頂部添加註釋來宣告檔案的編碼(UTF-8、Latin-1 或其他)。有關更多資訊,請參閱 PEP 263:原始碼編碼 一節。
透過
_tkinter
呼叫 Tcl 方法不再只返回字串。相反,如果 Tcl 返回其他物件,這些物件將被轉換為其 Python 等效項(如果存在),或者如果沒有 Python 等效項,則用_tkinter.Tcl_Obj
物件包裝。大的八進位制和十六進位制字面量,例如
0xffffffff
,現在會觸發FutureWarning
。目前它們儲存為 32 位數字並導致負值,但在 Python 2.4 中它們將成為正長整數。有幾種方法可以解決此警告。如果您確實需要一個正數,只需在字面量末尾新增一個
L
。如果您試圖獲得一個設定了低位的 32 位整數,並且以前使用過諸如~(1 << 31)
之類的表示式,那麼最清楚的方法可能是從所有位都設定開始,然後清除所需的上位。例如,要僅清除最高位(位 31),您可以編寫0xffffffffL &~(1L<<31)
。您不能再透過賦值給
__debug__
來停用斷言。Distutils 的
setup()
函式增加了一些新的關鍵字引數,例如 depends。舊版本的 Distutils 如果傳遞未知關鍵字將中止。解決方案是在您的setup.py
中檢查是否存在新的get_distutil_options()
函式,並且只在支援這些新關鍵字的 Distutils 版本中使用它們from distutils import core kw = {'sources': 'foo.c', ...} if hasattr(core, 'get_distutil_options'): kw['depends'] = ['foo.h'] ext = Extension(**kw)
使用
None
作為變數名現在將導致SyntaxWarning
警告。Python 中包含的模組定義的擴充套件型別的名稱現在包含模組名和型別名之前的
'.'
。
致謝¶
作者感謝以下人員對本文各種草稿提出建議、更正和協助:Jeff Bauer、Simon Brunning、Brett Cannon、Michael Chermside、Andrew Dalke、Scott David Daniels、Fred L. Drake, Jr.、David Fraser、Kelly Gerber、Raymond Hettinger、Michael Hudson、Chris Lambert、Detlef Lannert、Martin von Löwis、Andrew MacIntyre、Lalo Martins、Chad Netzer、Gustavo Niemeyer、Neal Norwitz、Hans Nowak、Chris Reedy、Francesco Ricciardi、Vinay Sajip、Neil Schemenauer、Roman Suzi、Jason Tishler、Just van Rossum。