Python 3.1 新特性

作者:

Raymond Hettinger

本文介紹了 Python 3.1 相對於 3.0 的新特性。Python 3.1 於 2009 年 6 月 27 日釋出。

PEP 372:有序字典

常規 Python 字典以任意順序迭代鍵/值對。多年來,許多作者編寫了替代實現,可以記住最初插入鍵的順序。基於這些實現的經驗,引入了一個新的 collections.OrderedDict 類。

OrderedDict API 與常規字典基本相同,但會根據鍵首次插入的時間以保證的順序迭代鍵和值。如果新條目覆蓋現有條目,則原始插入位置保持不變。刪除條目並重新插入會將其移動到末尾。

標準庫現在在多個模組中支援使用有序字典。configparser 模組預設使用它們。這使得配置檔案可以按照原始順序讀取、修改和寫回。collections.namedtuple()_asdict() 方法現在返回一個有序字典,其中的值與基礎元組索引的順序相同。json 模組正在使用 object_pairs_hook 進行構建,以允許解碼器構建 OrderedDicts。還增加了對 PyYAML 等第三方工具的支援。

另請參閱

PEP 372 - 有序字典

由 Armin Ronacher 和 Raymond Hettinger 編寫的 PEP。由 Raymond Hettinger 實現。

由於有序字典會記住其插入順序,因此可以與排序結合使用來建立排序字典

>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

新的排序字典在刪除條目時會保持其排序順序。但是,當新增新鍵時,鍵會追加到末尾,並且不會保持排序。

PEP 378:千位分隔符格式說明符

內建的 format() 函式和 str.format() 方法使用一種微型語言,現在包含一種簡單、不區分割槽域設定的方式來格式化帶有千位分隔符的數字。這提供了一種使程式輸出人性化的方法,提高了其專業外觀和可讀性

>>> format(1234567, ',d')
'1,234,567'
>>> format(1234567.89, ',.2f')
'1,234,567.89'
>>> format(12345.6 + 8901234.12j, ',f')
'12,345.600000+8,901,234.120000j'
>>> format(Decimal('1234567.89'), ',f')
'1,234,567.89'

支援的型別包括 intfloatcomplexdecimal.Decimal

正在討論如何指定諸如點、空格、撇號或下劃線之類的替代分隔符。區分割槽域設定的應用程式應使用現有的 n 格式說明符,該說明符已經對千位分隔符提供了一些支援。

另請參閱

PEP 378 - 千位分隔符格式說明符

由 Raymond Hettinger 編寫的 PEP,由 Eric Smith 和 Mark Dickinson 實現。

其他語言變更

對核心 Python 語言所做的一些較小的更改是

  • 現在,可以透過將包含 __main__.py 檔案的目錄和 zip 歸檔檔案直接傳遞給直譯器來執行它們。目錄/zip 檔案會自動作為 sys.path 中的第一個條目插入。(Andy Chu 的建議和初始補丁;Phillip J. Eby 和 Nick Coghlan 的修訂補丁;bpo-1739468。)

  • int() 型別獲得了一個 bit_length 方法,該方法返回以二進位制表示其引數所需的位數

    >>> n = 37
    >>> bin(37)
    '0b100101'
    >>> n.bit_length()
    6
    >>> n = 2**123-1
    >>> n.bit_length()
    123
    >>> (n+1).bit_length()
    124
    

    (由 Fredrik Johansson、Victor Stinner、Raymond Hettinger 和 Mark Dickinson 貢獻;bpo-3439。)

  • 現在可以自動編號 format() 字串中的欄位

    >>> 'Sir {} of {}'.format('Gallahad', 'Camelot')
    'Sir Gallahad of Camelot'
    

    以前,該字串將需要編號的欄位,例如:'Sir {0} of {1}'

    (由 Eric Smith 貢獻;bpo-5237。)

  • string.maketrans() 函式已棄用,並被新的靜態方法 bytes.maketrans()bytearray.maketrans() 替換。此更改解決了圍繞 string 模組支援哪些型別的困惑。現在,strbytesbytearray 各自都有自己的 maketranstranslate 方法,並帶有適當型別的中間轉換表。

    (由 Georg Brandl 貢獻;bpo-5675。)

  • with 語句的語法現在允許單個語句中的多個上下文管理器

    >>> with open('mylog.txt') as infile, open('a.out', 'w') as outfile:
    ...     for line in infile:
    ...         if '<critical>' in line:
    ...             outfile.write(line)
    

    使用新語法,不再需要 contextlib.nested() 函式,並且現在已棄用。

    (由 Georg Brandl 和 Mattias Brändström 貢獻;appspot issue 53094。)

  • 如果 x 是整數,則 round(x, n) 現在返回整數。以前它返回浮點數

    >>> round(1123, -2)
    1100
    

    (由 Mark Dickinson 貢獻;bpo-4707。)

  • Python 現在使用 David Gay 的演算法來查詢不會更改其值的最短浮點表示形式。這應該有助於緩解圍繞二進位制浮點數的一些困惑。

    對於像 1.1 這樣的數字,其意義很容易看到,因為二進位制浮點數中沒有完全等效的值。由於沒有完全等效的值,因此像 float('1.1') 這樣的表示式的求值結果為最接近的可表示值,該值在十六進位制中為 0x1.199999999999ap+0 或在十進位制中為 1.100000000000000088817841970012523233890533447265625。該最接近的值過去和現在都用於後續的浮點計算。

    新的是數字的顯示方式。以前,Python 使用一種簡單的方法。repr(1.1) 的值計算為 format(1.1, '.17g'),其求值結果為 '1.1000000000000001'。使用 17 位數字的優點在於,它依靠 IEEE-754 保證來確保 eval(repr(1.1)) 將精確地往返到其原始值。缺點是許多人發現輸出令人困惑(將二進位制浮點表示的內在侷限性誤認為是 Python 本身的問題)。

    repr(1.1) 的新演算法更智慧,並返回 '1.1'。實際上,它會搜尋所有等效的字串表示形式(使用相同的基礎浮點值儲存的表示形式),並返回最短的表示形式。

    新演算法傾向於在可能的情況下發出更簡潔的表示形式,但它不會更改基礎值。因此,即使表示形式可能暗示相反,1.1 + 2.2 != 3.3 的情況仍然存在。

    新演算法取決於基礎浮點實現中的某些功能。如果找不到所需的功能,將繼續使用舊演算法。此外,文字 pickle 協議透過使用舊演算法來保證跨平臺的可移植性。

    (由 Eric Smith 和 Mark Dickinson 貢獻;bpo-1580

新增、改進和已棄用的模組

  • 添加了 collections.Counter 類,以支援方便地統計序列或可迭代物件中唯一項的數量。

    >>> Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
    Counter({'blue': 3, 'red': 2, 'green': 1})
    

    (由 Raymond Hettinger 貢獻;bpo-1696199.)

  • 添加了一個新的模組 tkinter.ttk,用於訪問 Tk 主題化視窗部件集。ttk 的基本思想是將實現視窗部件行為的程式碼與實現其外觀的程式碼儘可能地分離。

    (由 Guilherme Polo 貢獻;bpo-2983.)

  • gzip.GzipFilebz2.BZ2File 類現在支援上下文管理協議。

    >>> # Automatically close file after writing
    >>> with gzip.GzipFile(filename, "wb") as f:
    ...     f.write(b"xxx")
    

    (由 Antoine Pitrou 貢獻.)

  • decimal 模組現在支援從二進位制 float 建立十進位制物件的方法。轉換是精確的,但有時可能會令人驚訝。

    >>> Decimal.from_float(1.1)
    Decimal('1.100000000000000088817841970012523233890533447265625')
    

    長的十進位制結果顯示了為 *1.1* 儲存的實際二進位制小數。該小數有很多位,因為 *1.1* 不能以二進位制精確表示。

    (由 Raymond Hettinger 和 Mark Dickinson 貢獻.)

  • itertools 模組增加了兩個新函式。itertools.combinations_with_replacement() 函式是生成包括排列和笛卡爾積在內的組合數學的四個函式之一。itertools.compress() 函式模仿了 APL 中的同名函式。此外,現有的 itertools.count() 函式現在有一個可選的 *step* 引數,並且可以接受任何型別的計數序列,包括 fractions.Fractiondecimal.Decimal

    >>> [p+q for p,q in combinations_with_replacement('LOVE', 2)]
    ['LL', 'LO', 'LV', 'LE', 'OO', 'OV', 'OE', 'VV', 'VE', 'EE']
    
    >>> list(compress(data=range(10), selectors=[0,0,1,1,0,1,0,1,0,0]))
    [2, 3, 5, 7]
    
    >>> c = count(start=Fraction(1,2), step=Fraction(1,6))
    >>> [next(c), next(c), next(c), next(c)]
    [Fraction(1, 2), Fraction(2, 3), Fraction(5, 6), Fraction(1, 1)]
    

    (由 Raymond Hettinger 貢獻.)

  • collections.namedtuple() 現在支援關鍵字引數 *rename*,它可以讓無效的欄位名自動轉換為 _0, _1 等形式的位置名稱。當欄位名是由外部來源(如 CSV 標頭、SQL 欄位列表或使用者輸入)建立時,這非常有用。

    >>> query = input()
    SELECT region, dept, count(*) FROM main GROUPBY region, dept
    
    >>> cursor.execute(query)
    >>> query_fields = [desc[0] for desc in cursor.description]
    >>> UserQuery = namedtuple('UserQuery', query_fields, rename=True)
    >>> pprint.pprint([UserQuery(*row) for row in cursor])
    [UserQuery(region='South', dept='Shipping', _2=185),
     UserQuery(region='North', dept='Accounting', _2=37),
     UserQuery(region='West', dept='Sales', _2=419)]
    

    (由 Raymond Hettinger 貢獻;bpo-1818.)

  • re.sub()re.subn()re.split() 函式現在接受一個 flags 引數。

    (由 Gregory Smith 貢獻.)

  • logging 模組現在實現了一個簡單的 logging.NullHandler 類,用於不使用日誌記錄但呼叫了日誌記錄的庫程式碼的應用程式。設定一個空處理器將抑制諸如 “No handlers could be found for logger foo” 之類的虛假警告。

    >>> h = logging.NullHandler()
    >>> logging.getLogger("foo").addHandler(h)
    

    (由 Vinay Sajip 貢獻;bpo-4384).

  • runpy 模組支援 -m 命令列開關,現在透過查詢並執行包名稱提供時的 __main__ 子模組來支援包的執行。

    (由 Andi Vajda 貢獻;bpo-4195.)

  • pdb 模組現在可以訪問和顯示透過 zipimport (或任何其他符合規範的 PEP 302 載入器)載入的原始碼。

    (由 Alexander Belopolsky 貢獻;bpo-4201.)

  • functools.partial 物件現在可以被 pickle 序列化。

(由 Antoine Pitrou 和 Jesse Noller 建議。由 Jack Diederich 實現;bpo-5228.)

  • 為符號新增 pydoc 幫助主題,以便 help('@') 在互動式環境中按預期工作。

    (由 David Laban 貢獻;bpo-4739.)

  • unittest 模組現在支援跳過單獨的測試或測試類。它還支援將測試標記為預期失敗,即已知存在問題的測試,但它不應在 TestResult 中被計為失敗。

    class TestGizmo(unittest.TestCase):
    
        @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
        def test_gizmo_on_windows(self):
            ...
    
        @unittest.expectedFailure
        def test_gimzo_without_required_library(self):
            ...
    

    此外,異常的測試已擴充套件到使用 with 語句與上下文管理器一起工作。

    def test_division_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            x / 0
    

    此外,還添加了一些新的斷言方法,包括 assertSetEqual(), assertDictEqual(), assertDictContainsSubset(), assertListEqual(), assertTupleEqual(), assertSequenceEqual(), assertRaisesRegexp(), assertIsNone(), 和 assertIsNotNone()

    (由 Benjamin Peterson 和 Antoine Pitrou 貢獻.)

  • io 模組為 seek() 方法添加了三個新的常量:SEEK_SETSEEK_CURSEEK_END

  • sys.version_info 元組現在是一個命名元組。

    >>> sys.version_info
    sys.version_info(major=3, minor=1, micro=0, releaselevel='alpha', serial=2)
    

    (由 Ross Light 貢獻;bpo-4285.)

  • nntplibimaplib 模組現在支援 IPv6。

    (由 Derek Morr 貢獻;bpo-1655bpo-1664.)

  • 當使用協議 2 或更低版本時,pickle 模組已進行了調整,以便更好地與 Python 2.x 互操作。標準庫的重組更改了許多物件的正式引用。例如,Python 2 中的 __builtin__.set 在 Python 3 中稱為 builtins.set。此更改混淆了在不同 Python 版本之間共享資料的努力。但是現在,當選擇協議 2 或更低版本時,pickler 將自動使用舊的 Python 2 名稱進行載入和轉儲。此重對映預設啟用,但可以使用 *fix_imports* 選項停用。

    >>> s = {1, 2, 3}
    >>> pickle.dumps(s, protocol=0)
    b'c__builtin__\nset\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    >>> pickle.dumps(s, protocol=0, fix_imports=False)
    b'cbuiltins\nset\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    

    此更改的一個不幸但不可避免的副作用是,Python 3.1 生成的協議 2 pickle 不能被 Python 3.0 讀取。在 Python 3.x 實現之間遷移資料時,應使用最新的 pickle 協議(協議 3),因為它不會嘗試與 Python 2.x 保持相容。

    (由 Alexandre Vassalotti 和 Antoine Pitrou 貢獻,bpo-6137.)

  • 添加了一個新的模組 importlib。它提供了一個完整、可移植、純 Python 的 import 語句及其對應函式 __import__() 的參考實現。它代表了在記錄和定義匯入過程中發生的操作方面向前邁出的一大步。

    (由 Brett Cannon 貢獻。)

最佳化

添加了主要的效能增強

  • 新的 I/O 庫(如 PEP 3116 中定義的那樣)主要用 Python 編寫,並很快被證明是 Python 3.0 中的一個瓶頸。在 Python 3.1 中,I/O 庫已完全用 C 重寫,速度提高了 2 到 20 倍,具體取決於手頭的任務。純 Python 版本仍然可以透過 _pyio 模組進行實驗。

    (由 Amaury Forgeot d'Arc 和 Antoine Pitrou 貢獻。)

  • 添加了一種啟發式方法,以便垃圾回收器不會跟蹤僅包含不可跟蹤物件的元組和字典。這可以減少集合的大小,從而減少長期執行的程式中垃圾回收的開銷,具體取決於它們對資料型別的特定使用。

    (由 Antoine Pitrou 貢獻,bpo-4688。)

  • 在支援它的編譯器(特別是:gcc、SunPro、icc)上啟用名為 --with-computed-gotos 的配置選項,位元組碼評估迴圈將使用新的排程機制進行編譯,根據系統、編譯器和基準測試,速度可提高高達 20%。

    (由 Antoine Pitrou 與其他多位參與者一起貢獻,bpo-4753)。

  • UTF-8、UTF-16 和 LATIN-1 的解碼速度現在快了 2 到 4 倍。

    (由 Antoine Pitrou 和 Amaury Forgeot d'Arc 貢獻,bpo-4868。)

  • json 模組現在有一個 C 擴充套件,以大幅提高其效能。此外,API 進行了修改,使 json 僅與 str 而不是 bytes 一起使用。該更改使該模組與以 Unicode 定義的 JSON 規範緊密匹配。

    (由 Bob Ippolito 貢獻,並由 Antoine Pitrou 和 Benjamin Peterson 轉換為 Py3.1;bpo-4136。)

  • 現在,反序列化會實習已序列化物件的屬性名稱。這可以節省記憶體並允許序列化檔案更小。

    (由 Jake McGuire 和 Antoine Pitrou 貢獻;bpo-5084。)

IDLE

  • IDLE 的格式選單現在提供一個選項來刪除原始檔中的尾隨空格。

    (由 Roger D. Serwy 貢獻;bpo-5150。)

構建和 C API 更改

對 Python 的構建過程和 C API 的更改包括

  • 整數現在在內部以 2**15 為底或以 2**30 為底儲存,底數在構建時確定。以前,它們始終以 2**15 為底儲存。在 64 位機器上使用以 2**30 為底可以顯著提高效能,但在 32 位機器上的基準測試結果好壞參半。因此,預設是在 64 位機器上使用以 2**30 為底,在 32 位機器上使用以 2**15 為底;在 Unix 上,有一個新的配置選項 --enable-big-digits 可用於覆蓋此預設值。

    除了效能改進之外,此更改對於終端使用者應該是不可見的,但有一個例外:出於測試和除錯目的,有一個新的 sys.int_info,它提供有關內部格式的資訊,給出每位數字的位數和用於儲存每個數字的 C 型別的大小(以位元組為單位)。

    >>> import sys
    >>> sys.int_info
    sys.int_info(bits_per_digit=30, sizeof_digit=4)
    

    (由 Mark Dickinson 貢獻;bpo-4258。)

  • PyLong_AsUnsignedLongLong() 函式現在透過引發 OverflowError 而不是 TypeError 來處理負的 pylong

    (由 Mark Dickinson 和 Lisandro Dalcrin 貢獻;bpo-5175。)

  • 已棄用 PyNumber_Int()。請改用 PyNumber_Long()

    (由 Mark Dickinson 貢獻;bpo-4910。)

  • 添加了一個新的 PyOS_string_to_double() 函式來替換已棄用的函式 PyOS_ascii_strtod()PyOS_ascii_atof()

    (由 Mark Dickinson 貢獻;bpo-5914。)

  • 添加了 PyCapsule 作為 PyCObject API 的替代品。主要區別在於,新型別具有明確定義的介面,用於傳遞型別安全資訊,以及更簡單的呼叫解構函式的簽名。舊型別具有有問題的 API,現在已棄用。

    (由 Larry Hastings 貢獻;bpo-5630。)

移植到 Python 3.1

本節列出了前面描述的更改和其他可能需要更改程式碼的錯誤修復

  • 新的浮點字串表示形式可能會破壞現有的 doctest。例如

    def e():
        '''Compute the base of natural logarithms.
    
        >>> e()
        2.7182818284590451
    
        '''
        return sum(1/math.factorial(x) for x in reversed(range(30)))
    
    doctest.testmod()
    
    **********************************************************************
    Failed example:
        e()
    Expected:
        2.7182818284590451
    Got:
        2.718281828459045
    **********************************************************************
    
  • pickle 模組中對於協議 2 或更低版本的自動名稱重新對映可能會使 Python 3.1 序列化檔案在 Python 3.0 中不可讀。一種解決方案是使用協議 3。另一種解決方案是將 fix_imports 選項設定為 False。有關更多詳細資訊,請參閱上面的討論。