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 - 新增內建集合物件型別

由 Greg V. Wilson 編寫的 PEP。由 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 語句。yieldreturn 語句之間的最大區別在於,當到達 yield 時,生成器的執行狀態會被掛起,並且區域性變數會被保留。在下次呼叫生成器的 .next() 方法時,函式將立即從 yield 語句之後恢復執行。(由於複雜的原因,不允許在 tryfinally 語句的 try 塊內部使用 yield 語句;請閱讀 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 中,每個表示式和函式呼叫都像一個生成器。“Icon 程式語言概述”(https://www2.cs.arizona.edu/icon/docs/ipd266.htm) 中的一個示例可以讓你瞭解它的樣子

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 時,可以透過在執行 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 都由一個名稱標識,並且名稱使用 . 作為元件分隔符組織成一個層次結構。例如,您可能有名為 serverserver.authserver.networkLogger 例項。後兩個例項在層次結構中位於 server 的下方。這意味著如果您提高 server 的詳細程度或將 server 訊息定向到不同的處理器,則更改也將應用於記錄到 server.authserver.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 的訊息也會被 serverroot 看到,但是 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__ 模組中,即 TrueFalse。(TrueFalse 常量已在 Python 2.2.1 中新增到內建函式中,但 2.2.1 版本只是簡單地設定為整數值 1 和 0,而不是不同的型別。)

此新型別的型別物件名為 bool;它的建構函式接受任何 Python 值並將其轉換為 TrueFalse

>>> 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

總而言之,用一句話概括 TrueFalse:它們是拼寫整數值 1 和 0 的另一種方式,唯一的區別在於 str()repr() 返回字串 'True''False' 而不是 '1''0'

另請參閱

PEP 285 - 添加布爾型別

由 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 獲得。

為了使目錄更有用,新的可選 classifiers 關鍵字引數已新增到 Distutils setup() 函式中。可以提供一個 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 模組以來,編寫自定義匯入鉤子一直都是可能的,但沒有人真正對此感到滿意,因為編寫新的匯入鉤子既困難又麻煩。已經有各種提議的替代方案,例如 imputiliu 模組,但它們都沒有獲得太多的認可,並且都沒有容易地從 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 將是一個模組或包名稱,例如 stringdistutils.corefind_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 增強

picklecPickle 模組在 2.3 開發週期中受到了一些關注。在 2.2 中,新式類可以毫無困難地進行 pickle 操作,但它們的 pickle 操作並不十分緊湊;PEP 307 引用了一個簡單的示例,其中一個新式類導致的 pickle 字串的長度是經典類的三倍。

解決方案是發明一種新的 pickle 協議。pickle.dumps() 函式長期以來都支援文字或二進位制標誌。在 2.3 中,此標誌從布林值重新定義為整數:0 是舊的文字模式 pickle 格式,1 是舊的二進位制格式,而現在 2 是新的 2.3 特定格式。可以使用新的常量 pickle.HIGHEST_PROTOCOL 來選擇可用的最先進的協議。

現在,取消 pickle 操作不再被認為是安全操作。2.2 的 pickle 提供了鉤子,用於嘗試阻止取消 pickle 不安全的類(特別是 __safe_for_unpickling__ 屬性),但沒有任何程式碼經過稽核,因此所有這些程式碼都在 2.3 中被刪除。在任何 Python 版本中,都不應該取消 pickle 不受信任的資料。

為了減少新式類的 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),給定一個序列的長度,該方法返回一個可以傳遞給 range()(start, stop, step) 元組。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 一致,其中 intstr 等經歷了相同的變化。

其他語言更改

以下是 Python 2.3 對核心 Python 語言所做的所有更改。

  • 如本文件的 PEP 255:簡單生成器 部分所述,yield 語句現在始終是一個關鍵字。

  • 如本文件的 PEP 279:enumerate() 部分所述,添加了一個新的內建函式 enumerate()

  • 如本文件的 PEP 285:布林型別 部分所述,添加了兩個新的常量 TrueFalse 以及內建的 bool 型別。

  • 當字串或浮點數太大而無法放入整數時,int() 型別建構函式現在將返回一個長整數,而不是引發 OverflowError。這可能會導致 isinstance(int(expression), int) 為 false 的自相矛盾的結果,但這似乎不太可能在實踐中引起問題。

  • 內建型別現在支援擴充套件切片語法,如本文件的 擴充套件切片 部分所述。

  • 一個新的內建函式 sum(iterable, start=0),它將可迭代物件中的數字項相加並返回它們的和。sum() 只接受數字,這意味著不能用它來連線一堆字串。(由 Alex Martelli 貢獻。)

  • pos 為負數時,list.insert(pos, value) 過去會在列表的前面插入 value。現在該行為已更改為與切片索引一致,因此當 pos 為 -1 時,該值將插入到最後一個元素之前,依此類推。

  • list.index(value) 在列表中搜索 value 並返回其索引,現在採用可選的 startstop 引數,以將搜尋限制到列表的某個部分。

  • 字典有一個新方法 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 貢獻。)

  • >>> import types
    >>> m = types.ModuleType('abc','docstring')
    >>> m
    <module 'abc' (built-in)>
    >>> m.__doc__
    'docstring'
    
  • >>> s = socket.socket()
    >>> s.__class__
    <type 'socket'>
    

    >>> s.__class__
    <type '_socket.socket'>
    

  • >>> 'ab' in 'abcd'
    True
    >>> 'ad' in 'abcd'
    False
    >>> '' in 'abcd'
    True
    

  • >>> '   abc '.strip()
    'abc'
    >>> '><><abc<><><>'.strip('<>')
    'abc'
    >>> '><><abc<><><>\n'.strip('<>')
    'abc<><><>\n'
    >>> u'\u4000\u4001abc\u4000'.strip(u'\u4000')
    u'\u4001abc'
    >>>
    

  • >>> '45'.zfill(4)
    '0045'
    >>> '12345'.zfill(4)
    '12345'
    >>> 'goofy'.zfill(6)
    '0goofy'
    

  • array 模組現在支援使用 'u' 格式字元的 Unicode 字元陣列。陣列現在還支援使用 += 賦值運算子來新增另一個數組的內容,以及使用 *= 賦值運算子來重複一個數組。(由 Jason Orendorff 貢獻)。

  • bsddb 模組已被 PyBSDDB 包的 4.1.6 版本替換,為 BerkeleyDB 庫的事務功能提供了更完整的介面。

    舊版本的模組已重新命名為 bsddb185,並且不再自動構建;您需要編輯 Modules/Setup 來啟用它。請注意,新的 bsddb 包旨在與舊模組相容,因此如果您發現任何不相容之處,請務必提交錯誤報告。當升級到 Python 2.3 時,如果新的直譯器是使用底層 BerkeleyDB 庫的新版本編譯的,那麼您幾乎肯定需要將資料庫檔案轉換為新版本。您可以使用發行版的 Tools/scripts 目錄中的新指令碼 db2pickle.pypickle2db.py 輕鬆完成此操作。如果您之前一直在使用 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 的其他一些小改動:它現在檢查 CCCFLAGSCPPLDFLAGSCPPFLAGS 環境變數,使用它們來覆蓋 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 貢獻)。

  • grppwdresource 模組現在返回增強的元組

    >>> import grp
    >>> g = grp.getgrnam('amk')
    >>> g.gr_name, g.gr_gid
    ('amk', 500)
    
  • gzip 模組現在可以處理超過 2 GiB 的檔案。

  • 新的 heapq 模組包含堆佇列演算法的實現。堆是一種類似陣列的資料結構,它將專案保持在部分排序的順序,使得對於每個索引 kheap[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 引數,以便更容易計算以 e10 以外的數為底的對數。(由 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 模組現在使用一種新的演算法,即用 C 實現的梅森旋轉演算法。它比以前的演算法更快,並且經過更廣泛的研究。

    (所有更改均由 Raymond Hettinger 貢獻。)

  • readline 模組也獲得了一些新函式:get_history_item()get_current_history_length()redisplay()

  • rexecBastion 模組已被宣佈為已失效,嘗試匯入它們將失敗並出現 RuntimeError。新型類提供了突破 rexec 提供的受限執行環境的新方法,並且沒有人有興趣修復它們或有時間這樣做。如果您的應用程式正在使用 rexec,請將其重寫為使用其他替代方案。

    (堅持使用 Python 2.2 或 2.1 並不會使您的應用程式更安全,因為在這些版本的 rexec 模組中存在已知錯誤。再次強調:如果您正在使用 rexec,請立即停止使用它。)

  • rotor 模組已被棄用,因為它用於加密的演算法被認為是不安全的。如果您需要加密,請使用單獨提供的幾個 AES Python 模組之一。

  • shutil 模組獲得了一個 move(src, dest) 函式,該函式以遞迴方式將檔案或目錄移動到新位置。

  • signal 添加了對更高階的 POSIX 訊號處理的支援,但後來又將其刪除,因為它被證明無法在各個平臺上可靠地工作。

  • socket 模組現在支援超時。您可以在套接字物件上呼叫 settimeout(t) 方法來設定 t 秒的超時。隨後的套接字操作如果耗時超過 t 秒完成,將會中止並引發 socket.timeout 異常。

    最初的超時實現由 Tim O’Malley 完成。Michael Gilfix 將其整合到 Python 的 socket 模組中,並引導它通過了漫長的審查。程式碼被檢入後,Guido van Rossum 重寫了其中的一部分。(這是正在進行的協作開發過程的一個很好的例子。)

  • 在 Windows 上,socket 模組現在附帶了安全套接層 (SSL) 支援。

  • C 宏 PYTHON_API_VERSION 的值現在以 sys.api_version 的形式在 Python 級別公開。可以透過呼叫新的 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 貢獻。)

  • threadthreading 模組現在有了配套模組,dummy_threaddummy_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 類。這是一個簡短的示例,用於確定透過將空 Unicode 字串附加到 8 位字串還是使用 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 的執行緒模型要求只能從建立視窗小部件的執行緒訪問視窗小部件;從另一個執行緒訪問可能會導致 Tcl 崩潰。對於某些 Tcl 介面,當從不同的執行緒訪問視窗小部件時,Tkinter 現在會自動避免這種情況,方法是編組一個命令,將其傳遞到正確的執行緒,並等待結果。其他介面無法自動處理,但是 Tkinter 現在會在此類訪問時引發異常,以便您至少可以找出問題。有關此更改的更詳細說明,請參見 https://mail.python.org/pipermail/python-dev/2002-December/031107.html。(由 Martin von Löwis 實現。)

  • 透過 _tkinter 呼叫 Tcl 方法不再只返回字串。相反,如果 Tcl 返回其他物件,則這些物件將轉換為其 Python 等效項(如果存在),或者如果沒有 Python 等效項,則用 _tkinter.Tcl_Obj 物件包裝。可以透過 tkapp 物件的 wantobjects() 方法來控制此行為。

    當透過 Tkinter 模組(大多數 Tkinter 應用程式將使用)使用 _tkinter 時,此功能始終處於啟用狀態。它不應引起相容性問題,因為 Tkinter 會盡可能將字串結果轉換為 Python 型別。

    如果發現任何不相容性,可以透過在建立第一個 tkapp 物件之前將 Tkinter 模組中的 wantobjects 變數設定為 false 來恢復舊的行為。

    import Tkinter
    Tkinter.wantobjects = 0
    

    由此更改引起的任何中斷都應報告為錯誤。

  • UserDict 模組有一個新的 DictMixin 類,它為已經具有最小對映介面的類定義了所有字典方法。這大大簡化了編寫需要替代字典的類(例如 shelve 模組中的類)。

    只要類定義了 __getitem__()__setitem__()__delitem__()keys(),則將 mix-in 作為超類可以提供完整的字典介面。例如

    >>> 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 版本。處理主機名的模組(例如 httplibftplib)也支援 Unicode 主機名;httplib 還使用域名的 ACE 版本傳送 HTTP Host 標頭。urllib 支援帶有非 ASCII 主機名的 Unicode URL,只要 URL 的 path 部分僅包含 ASCII 字元即可。

    為了實現此更改,已新增 stringprep 模組、mkstringprep 工具和 punycode 編碼。

日期/時間型別

適合表示時間戳的日期和時間型別已作為 datetime 模組新增。這些型別不支援不同的日曆或許多花哨的功能,只是堅持表示時間的基礎知識。

三個主要型別是:date,表示日、月和年;time,由小時、分鐘和秒組成;以及 datetime,它包含 datetime 的所有屬性。還有一個 timedelta 類,表示時間中兩個點之間的差異,並且時區邏輯由繼承自抽象 tzinfo 類的類實現。

您可以透過向相應的建構函式提供關鍵字引數來建立 datetime 的例項,例如 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() 方法允許修改 datedatetime 例項的一個或多個欄位,並返回一個新例項

>>> 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() 的結果相同)。datedatetime 例項可以彼此相減,並新增到 timedelta 例項。最大的缺失功能是標準庫不支援解析字串並返回 datedatetime

有關更多資訊,請參閱模組的參考文件。(由 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 物件。

得益於 Tim Peters 的大量工作,2.3 中的 pymalloc 還提供了除錯功能,以捕獲擴充套件模組和直譯器本身的記憶體覆蓋和雙重釋放。要啟用此支援,請透過執行帶有 --with-pydebugconfigure 來編譯 Python 直譯器的除錯版本。

為了幫助擴充套件編寫者,Python 2.3 的原始碼附帶了一個頭檔案 Misc/pymemcompat.h,允許 Python 擴充套件使用 2.3 記憶體分配介面,同時針對自 1.5.2 以來的任何 Python 版本進行編譯。您需要從 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_EXPORTDL_IMPORT 宏現在已棄用。Python 擴充套件模組的初始化函式現在應該使用新的宏 PyMODINIT_FUNC 進行宣告,而 Python 核心通常會使用 PyAPI_FUNCPyAPI_DATA 宏。

  • 可以透過向 configure 指令碼提供 --without-doc-strings 來編譯不包含內建函式和模組的文件字串的直譯器。這會使 Python 可執行檔案縮小大約 10%,但這也意味著您無法獲得 Python 內建函式的幫助。(由 Gustavo Niemeyer 貢獻。)

  • PyArg_NoArgs() 宏現在已棄用,應更改使用它的程式碼。對於 Python 2.2 及更高版本,方法定義表可以指定 METH_NOARGS 標誌,表示沒有引數,然後可以刪除引數檢查。如果與 Python 2.2 之前的版本相容很重要,則程式碼可以使用 PyArg_ParseTuple(args, ""),但這會比使用 METH_NOARGS 慢。

  • PyArg_ParseTuple() 接受各種大小的無符號整數的新格式字元:B 代表 unsigned charH 代表 unsigned short intI 代表 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_CLASSMETH_STATIC 標誌來為 C 擴充套件型別定義類方法和靜態方法。

  • Python 現在包含 Expat XML 解析器原始碼的副本,從而消除了對系統版本或 Expat 本地安裝的任何依賴。

  • 如果您在擴充套件中動態分配型別物件,則應注意與 __module____name__ 屬性相關的規則的更改。總而言之,您需要確保型別的字典包含 '__module__' 鍵;使模組名稱成為型別名稱中最後一個句點之前的部分將不再具有所需的效果。有關更多詳細資訊,請閱讀 API 參考文件或原始碼。

特定於埠的更改

支援使用 EMX 執行時環境移植到 IBM 的 OS/2 的程式碼已合併到主 Python 原始碼樹中。EMX 是 OS/2 系統 API 之上的 POSIX 模擬層。EMX 的 Python 埠嘗試支援 EMX 執行時公開的所有類似 POSIX 的功能,並且大部分都成功了;fork()fcntl() 受底層模擬層的限制。標準 OS/2 埠(使用 IBM 的 Visual Age 編譯器)也獲得了對區分大小寫的匯入語義的支援,作為 EMX 埠整合到 CVS 的一部分。(由 Andrew MacIntyre 貢獻。)

在 MacOS 上,大多數工具箱模組都已弱連結,以提高向後相容性。這意味著如果當前作業系統版本缺少單個例程,模組將不再載入失敗。相反,呼叫缺少的例程將引發異常。(由 Jack Jansen 貢獻。)

在 Python 原始碼發行版的 Misc/RPM/ 目錄中找到的 RPM spec 檔案已針對 2.3 更新。(由 Sean Reifschneider 貢獻。)

Python 現在支援的其他新平臺包括 AtheOS (http://www.atheos.cx/)、GNU/Hurd 和 OpenVMS。

其他更改和修復

與往常一樣,整個原始碼樹中分散著許多其他改進和錯誤修復。搜尋 CVS 更改日誌發現,在 Python 2.2 和 2.3 之間應用了 523 個補丁並修復了 514 個錯誤。這兩個數字都可能被低估了。

一些更值得注意的更改是

  • 如果設定了 PYTHONINSPECT 環境變數,則 Python 直譯器將在執行 Python 程式後進入互動式提示符,就像使用 -i 選項呼叫 Python 一樣。可以在執行 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 屬性,從而更改接下來將要執行的行。一個 jump 命令已新增到 pdb 偵錯程式中,利用了這個新特性。(由 Richie Hindle 實現。)

移植到 Python 2.3

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

  • yield 現在始終是一個關鍵字;如果它在您的程式碼中用作變數名,則必須選擇不同的名稱。

  • 對於字串 XY,如果 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。