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 語句。yieldreturn 語句之間的最大區別在於,到達 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 都由一個名稱標識,名稱使用 . 作為元件分隔符組織成一個層次結構。例如,您可能擁有名為 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 - 新增 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 模組以來,就可以編寫自定義匯入鉤子,但沒有人真正滿意,因為編寫新的匯入鉤子既困難又混亂。曾有各種替代方案被提出,例如 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 可以用來選擇可用的最複雜的協議。

解包不再被認為是安全操作。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 保持一致,其中 intstr 等也經歷了相同的變化。

其他語言更改

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

  • yield 語句現在始終是關鍵字,如本文件的 PEP 255: Simple Generators 一節所述。

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

  • 新增了兩個常量,TrueFalse,以及內建的 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.pypickle2db.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 的其他次要更改:它現在檢查 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 模組現在使用 Mersenne Twister 演算法(用 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) 函式,該函式遞迴地將檔案或目錄移動到新位置。

  • 對更高階的 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 貢獻。)

  • 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 類可以直接匯入和使用。這是一個簡短的示例,用於判斷是將 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 版本。處理主機名的模組(例如 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 還提供了除錯功能,可以在擴充套件模組和直譯器本身中捕獲記憶體覆蓋和雙重釋放。要啟用此支援,請透過執行 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_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 標誌,表示沒有引數,然後可以刪除引數檢查。如果與 2.2 之前的 Python 版本相容很重要,程式碼可以使用 PyArg_ParseTuple(args, "") 代替,但這會比使用 METH_NOARGS 慢。

  • PyArg_ParseTuple() 接受用於各種大小的無符號整數的新格式字元:B 代表 unsigned charH 代表 unsigned short intI 代表 unsigned intK 代表 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 參考文件或原始碼。

特定於埠的更改

對 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 現在始終是一個關鍵字;如果它在您的程式碼中用作變數名,則必須選擇不同的名稱。

  • 對於字串 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。