Python 3.8 中的新特性

編輯:

Raymond Hettinger

本文解釋了 Python 3.8 相較於 3.7 的新特性。Python 3.8 於 2019 年 10 月 14 日釋出。有關完整詳細資訊,請參閱更新日誌

摘要 – 釋出亮點

新功能

賦值表示式

引入了新的語法 :=,作為更大表達式的一部分將值賦給變數。由於其與海象的眼睛和象牙的相似之處,它被親切地稱為“海象運算子”。

在此示例中,賦值表示式有助於避免兩次呼叫 len()

if (n := len(a)) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")

在正則表示式匹配中也會出現類似的優勢,其中匹配物件需要兩次,一次用於測試是否發生匹配,另一次用於提取子組

discount = 0.0
if (mo := re.search(r'(\d+)% discount', advertisement)):
    discount = float(mo.group(1)) / 100.0

該運算子對於 while 迴圈也很有用,這些迴圈計算一個值來測試迴圈終止,然後又在迴圈體中需要該相同的值

# Loop over fixed length blocks
while (block := f.read(256)) != '':
    process(block)

另一個激勵性的用例出現在列表推導中,其中過濾條件中計算的值也需要用於表示式主體

[clean_name.title() for name in names
 if (clean_name := normalize('NFC', name)) in allowed_names]

儘量將海象運算子的使用限制在減少複雜性並提高可讀性的清晰場景中。

有關完整描述,請參閱PEP 572

(由 Emily Morehouse 在bpo-35224中貢獻。)

僅限位置引數

引入了新的函式引數語法 /,表示某些函式引數必須按位置指定,不能用作關鍵字引數。這與 help() 為使用 Larry Hastings 的 Argument Clinic 工具註解的 C 函式所顯示的符號相同。

在以下示例中,引數 *a* 和 *b* 僅限位置,而 *c* 或 *d* 可以是位置或關鍵字,*e* 或 *f* 必須是關鍵字

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

以下是有效的呼叫

f(10, 20, 30, d=40, e=50, f=60)

然而,以下是無效的呼叫

f(10, b=20, c=30, d=40, e=50, f=60)   # b cannot be a keyword argument
f(10, 20, 30, 40, 50, f=60)           # e must be a keyword argument

這種符號的一個用例是它允許純 Python 函式完全模擬現有 C 編碼函式的行為。例如,內建的 divmod() 函式不接受關鍵字引數

def divmod(a, b, /):
    "Emulate the built in divmod() function"
    return (a // b, a % b)

另一個用例是在引數名稱沒有幫助時排除關鍵字引數。例如,內建的 len() 函式的簽名是 len(obj, /)。這排除了諸如以下笨拙的呼叫

len(obj='hello')  # The "obj" keyword argument impairs readability

將引數標記為僅限位置的另一個好處是,它允許將來更改引數名稱,而不會破壞客戶端程式碼。例如,在 statistics 模組中,引數名稱 *dist* 將來可能會更改。這透過以下函式規範成為可能

def quantiles(dist, /, *, n=4, method='exclusive')
    ...

由於 / 左側的引數不會暴露為可能的關鍵字,因此引數名稱仍可用於 **kwargs

>>> def f(a, b, /, **kwargs):
...     print(a, b, kwargs)
...
>>> f(10, 20, a=1, b=2, c=3)         # a and b are used in two ways
10 20 {'a': 1, 'b': 2, 'c': 3}

這大大簡化了需要接受任意關鍵字引數的函式和方法的實現。例如,這是 collections 模組中的程式碼摘錄

class Counter(dict):

    def __init__(self, iterable=None, /, **kwds):
        # Note "iterable" is a possible keyword argument

有關完整描述,請參閱PEP 570

(由 Pablo Galindo 在bpo-36540中貢獻。)

已編譯位元組碼檔案的並行檔案系統快取

新的 PYTHONPYCACHEPREFIX 設定(也可用作 -X pycache_prefix)將隱式位元組碼快取配置為使用單獨的並行檔案系統樹,而不是每個源目錄中預設的 __pycache__ 子目錄。

快取的位置在 sys.pycache_prefix 中報告(None 表示在 __pycache__ 子目錄中的預設位置)。

(由 Carl Meyer 在bpo-33499中貢獻。)

除錯構建使用與釋出構建相同的 ABI

無論是以釋出模式還是除錯模式構建,Python 現在都使用相同的 ABI。在 Unix 上,當 Python 以除錯模式構建時,現在可以載入以釋出模式構建的 C 擴充套件和使用穩定 ABI 構建的 C 擴充套件。

釋出構建和除錯構建現在是 ABI 相容的:定義 Py_DEBUG 宏不再意味著 Py_TRACE_REFS 宏,後者引入了唯一的 ABI 不相容性。Py_TRACE_REFS 宏添加了 sys.getobjects() 函式和 PYTHONDUMPREFS 環境變數,可以使用新的 ./configure --with-trace-refs 構建選項設定。(由 Victor Stinner 在bpo-36465中貢獻。)

在 Unix 上,C 擴充套件不再連結到 libpython,除了 Android 和 Cygwin。現在,靜態連結的 Python 可以載入使用共享庫 Python 構建的 C 擴充套件。(由 Victor Stinner 在bpo-21536中貢獻。)

在 Unix 上,當 Python 以除錯模式構建時,匯入現在還會查詢以釋出模式編譯的 C 擴充套件和以穩定 ABI 編譯的 C 擴充套件。(由 Victor Stinner 在bpo-36722中貢獻。)

要將 Python 嵌入到應用程式中,必須將新的 --embed 選項傳遞給 python3-config --libs --embed 以獲取 -lpython3.8(將應用程式連結到 libpython)。為了支援 3.8 及更早版本,請首先嚐試 python3-config --libs --embed,如果前一個命令失敗,則回退到 python3-config --libs(不帶 --embed)。

新增一個 pkg-config python-3.8-embed 模組,用於將 Python 嵌入到應用程式中:pkg-config python-3.8-embed --libs 包括 -lpython3.8。為了支援 3.8 及更早版本,請首先嚐試 pkg-config python-X.Y-embed --libs,如果前一個命令失敗,則回退到 pkg-config python-X.Y --libs(不帶 --embed)(將 X.Y 替換為 Python 版本)。

另一方面,pkg-config python3.8 --libs 不再包含 -lpython3.8。C 擴充套件不得連結到 libpython(Android 和 Cygwin 除外,它們的情況由指令碼處理);此更改是故意向後不相容的。(由 Victor Stinner 在bpo-36721中貢獻。)

f-字串支援 = 用於自文件化表示式和除錯

f-字串添加了 = 指定符。諸如 f'{expr=}' 這樣的 f-字串將擴充套件為表示式的文字、等號,然後是求值表示式的表示。例如

>>> user = 'eric_idle'
>>> member_since = date(1975, 7, 31)
>>> f'{user=} {member_since=}'
"user='eric_idle' member_since=datetime.date(1975, 7, 31)"

通常的f-字串格式說明符允許對錶達式結果的顯示方式進行更多控制

>>> delta = date.today() - member_since
>>> f'{user=!s}  {delta.days=:,d}'
'user=eric_idle  delta.days=16,075'

= 指定符將顯示整個表示式,以便可以顯示計算結果

>>> print(f'{theta=}  {cos(radians(theta))=:.3f}')
theta=30  cos(radians(theta))=0.866

(由 Eric V. Smith 和 Larry Hastings 在bpo-36817中貢獻。)

PEP 578: Python 執行時審計鉤子

該 PEP 添加了一個審計鉤子和驗證開放鉤子。兩者都可從 Python 和本機程式碼訪問,允許用純 Python 程式碼編寫的應用程式和框架利用額外的通知,同時還允許嵌入器或系統管理員部署始終啟用審計的 Python 構建。

有關完整詳細資訊,請參閱PEP 578

PEP 587: Python 初始化配置

PEP 587 添加了一個新的 C API 來配置 Python 初始化,提供對整個配置的更精細控制和更好的錯誤報告。

新結構

新函式

此 PEP 還將 _PyRuntimeState.preconfig (PyPreConfig 型別) 和 PyInterpreterState.config (PyConfig 型別) 欄位新增到這些內部結構中。PyInterpreterState.config 成為新的參考配置,取代了全域性配置變數和其他私有變數。

有關文件,請參閱Python 初始化配置

有關完整描述,請參閱PEP 587

(由 Victor Stinner 在bpo-36763中貢獻。)

PEP 590: Vectorcall: CPython 的快速呼叫協議

Vectorcall 協議已新增到 Python/C API 中。它旨在規範化已經針對各種類進行的現有最佳化。任何實現可呼叫物件的靜態型別都可以使用此協議。

這目前是臨時性的。目標是在 Python 3.9 中使其完全公開。

有關完整描述,請參閱PEP 590

(由 Jeroen Demeyer, Mark Shannon 和 Petr Viktorin 在bpo-36974中貢獻。)

Pickle 協議 5,帶有帶外資料緩衝區

當使用 pickle 在 Python 程序之間傳輸大量資料以利用多核或多機處理時,透過減少記憶體複製以及可能應用資料相關壓縮等自定義技術來最佳化傳輸至關重要。

pickle 協議 5 引入了對帶外緩衝區的支援,其中PEP 3118 相容資料可以根據通訊層的判斷與主 pickle 流分開傳輸。

有關完整描述,請參閱PEP 574

(由 Antoine Pitrou 在bpo-36785中貢獻。)

其他語言更改

  • 由於實現問題,continue 語句在 finally 子句中是非法的。在 Python 3.8 中,此限制被解除。(由 Serhiy Storchaka 在bpo-32489中貢獻。)

  • boolintfractions.Fraction 型別現在擁有 as_integer_ratio() 方法,與 floatdecimal.Decimal 中發現的類似。此次小的 API 擴充套件使得可以編寫 numerator, denominator = x.as_integer_ratio(),並使其適用於多種數字型別。(由 Lisa Roach 在bpo-33073和 Raymond Hettinger 在bpo-37819中貢獻。)

  • intfloatcomplex 的建構函式現在將使用特殊的 __index__() 方法,如果可用且相應的 __int__()__float__()__complex__() 方法不可用。(由 Serhiy Storchaka 在bpo-20092中貢獻。)

  • 正則表示式中添加了對 \N{name} 轉義序列的支援

    >>> notice = 'Copyright © 2019'
    >>> copyright_year_pattern = re.compile(r'\N{copyright sign}\s*(\d{4})')
    >>> int(copyright_year_pattern.search(notice).group(1))
    2019
    

    (由 Jonathan Eunice 和 Serhiy Storchaka 在bpo-30688中貢獻。)

  • Dict 和 dictviews 現在可以使用 reversed() 按反向插入順序進行迭代。(由 Rémi Lapeyre 在bpo-33462中貢獻。)

  • 函式呼叫中關鍵字名稱允許的語法被進一步限制。特別是,不再允許 f((keyword)=arg)。它從未打算允許關鍵字引數賦值項的左側出現裸名以外的更多內容。(由 Benjamin Peterson 在bpo-34641中貢獻。)

  • yieldreturn 語句中的通用可迭代解包不再需要用括號括起來。這使得 *yield* 和 *return* 語法與普通賦值語法更好地保持一致

    >>> def parse(family):
    ...     lastname, *members = family.split()
    ...     return lastname.upper(), *members
    ...
    >>> parse('simpsons homer marge bart lisa maggie')
    ('SIMPSONS', 'homer', 'marge', 'bart', 'lisa', 'maggie')
    

    (由 David Cuthbert 和 Jordan Chapman 在bpo-32117中貢獻。)

  • 當代碼中遺漏逗號時,例如 [(10, 20) (30, 40)],編譯器會顯示一個帶有有用建議的 SyntaxWarning。這比僅顯示一個指示第一個元組不可呼叫的 TypeError 更好。(由 Serhiy Storchaka 在bpo-15248中貢獻。)

  • datetime.datedatetime.datetime 的子類與 datetime.timedelta 物件之間的算術運算現在返回子類的例項,而不是基類。這也影響了其實現(直接或間接)使用 datetime.timedelta 算術運算的操作的返回型別,例如 astimezone()。(由 Paul Ganssle 在bpo-32417中貢獻。)

  • 當 Python 直譯器被 Ctrl-C (SIGINT) 中斷並且由此產生的 KeyboardInterrupt 異常未被捕獲時,Python 程序現在透過 SIGINT 訊號或以正確的退出程式碼退出,以便呼叫程序可以檢測到它因 Ctrl-C 而終止。POSIX 和 Windows 上的 shell 使用此方法在互動式會話中正確終止指令碼。(由 Google 經 Gregory P. Smith 在bpo-1054041中貢獻。)

  • 一些高階程式設計風格需要更新現有函式的 types.CodeType 物件。由於程式碼物件是不可變的,因此需要建立一個新的程式碼物件,該物件基於現有程式碼物件建模。有 19 個引數,這有些繁瑣。現在,新的 replace() 方法使得可以建立一個具有少量更改引數的克隆。

    這是一個示例,它修改了 statistics.mean() 函式以防止 *data* 引數用作關鍵字引數

    >>> from statistics import mean
    >>> mean(data=[10, 20, 90])
    40
    >>> mean.__code__ = mean.__code__.replace(co_posonlyargcount=1)
    >>> mean(data=[10, 20, 90])
    Traceback (most recent call last):
      ...
    TypeError: mean() got some positional-only arguments passed as keyword arguments: 'data'
    

    (由 Victor Stinner 在bpo-37032中貢獻。)

  • 對於整數,pow() 函式的三引數形式現在允許在基數與模數互質的情況下指數為負數。然後,當指數為 -1 時,它計算基數的模逆,對於其他負指數,則計算該逆的適當冪。例如,要計算 38 模 137 的模乘逆,請編寫

    >>> pow(38, -1, 137)
    119
    >>> 119 * 38 % 137
    1
    

    模逆出現在線性丟番圖方程的解中。例如,要找到 4258𝑥 + 147𝑦 = 369 的整數解,首先改寫為 4258𝑥 369 (mod 147),然後求解

    >>> x = 369 * pow(4258, -1, 147) % 147
    >>> y = (4258 * x - 369) // -147
    >>> 4258 * x + 147 * y
    369
    

    (由 Mark Dickinson 在bpo-36027中貢獻。)

  • 字典推導式已與字典字面量同步,以便首先計算鍵,然後計算值

    >>> # Dict comprehension
    >>> cast = {input('role? '): input('actor? ') for i in range(2)}
    role? King Arthur
    actor? Chapman
    role? Black Knight
    actor? Cleese
    
    >>> # Dict literal
    >>> cast = {input('role? '): input('actor? ')}
    role? Sir Robin
    actor? Eric Idle
    

    保證的執行順序對於賦值表示式很有幫助,因為在鍵表示式中賦值的變數將在值表示式中可用

    >>> names = ['Martin von Löwis', 'Łukasz Langa', 'Walter Dörwald']
    >>> {(n := normalize('NFC', name)).casefold() : n for name in names}
    {'martin von löwis': 'Martin von Löwis',
     'łukasz langa': 'Łukasz Langa',
     'walter dörwald': 'Walter Dörwald'}
    

    (由 Jörn Heissler 在bpo-35224中貢獻。)

  • object.__reduce__() 方法現在可以返回一個包含兩到六個元素的元組。以前,限制是五個。新的可選第六個元素是一個可呼叫物件,其簽名是 (obj, state)。這允許直接控制特定物件的狀態更新行為。如果不是 *None*,這個可呼叫物件將優先於物件的 __setstate__() 方法。(由 Pierre Glaser 和 Olivier Grisel 在bpo-35900中貢獻。)

新模組

  • 新的 importlib.metadata 模組提供了(臨時)支援從第三方包讀取元資料。例如,它可以提取已安裝包的版本號、入口點列表等

    >>> # Note following example requires that the popular "requests"
    >>> # package has been installed.
    >>>
    >>> from importlib.metadata import version, requires, files
    >>> version('requests')
    '2.22.0'
    >>> list(requires('requests'))
    ['chardet (<3.1.0,>=3.0.2)']
    >>> list(files('requests'))[:5]
    [PackagePath('requests-2.22.0.dist-info/INSTALLER'),
     PackagePath('requests-2.22.0.dist-info/LICENSE'),
     PackagePath('requests-2.22.0.dist-info/METADATA'),
     PackagePath('requests-2.22.0.dist-info/RECORD'),
     PackagePath('requests-2.22.0.dist-info/WHEEL')]
    

    (由 Barry Warsaw 和 Jason R. Coombs 在bpo-34632中貢獻。)

改進的模組

ast

AST 節點現在具有 end_linenoend_col_offset 屬性,它們提供節點結束的精確位置。(這僅適用於具有 linenocol_offset 屬性的節點。)

新函式 ast.get_source_segment() 返回特定 AST 節點的原始碼。

(由 Ivan Levkivskyi 在bpo-33416中貢獻。)

ast.parse() 函式有一些新標誌

  • type_comments=True 使其返回與某些 AST 節點關聯的PEP 484PEP 526 型別註釋的文字;

  • mode='func_type' 可用於解析PEP 484“簽名型別註釋”(返回給函式定義 AST 節點);

  • feature_version=(3, N) 允許指定較早的 Python 3 版本。例如,feature_version=(3, 4) 將把 asyncawait 視為非保留字。

(由 Guido van Rossum 在bpo-35766中貢獻。)

asyncio

asyncio.run() 已從臨時 API 升級為穩定 API。此函式可用於執行協程並返回結果,同時自動管理事件迴圈。例如

import asyncio

async def main():
    await asyncio.sleep(0)
    return 42

asyncio.run(main())

這 *大致* 等同於

import asyncio

async def main():
    await asyncio.sleep(0)
    return 42

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
    loop.run_until_complete(main())
finally:
    asyncio.set_event_loop(None)
    loop.close()

實際實現要複雜得多。因此,asyncio.run() 應該是執行 asyncio 程式的首選方式。

(由 Yury Selivanov 在bpo-32314中貢獻。)

執行 python -m asyncio 啟動一個本地非同步 REPL。這允許快速試驗具有頂級 await 的程式碼。不再需要直接呼叫 asyncio.run(),後者會在每次呼叫時生成一個新的事件迴圈

$ python -m asyncio
asyncio REPL 3.8.0
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> await asyncio.sleep(10, result='hello')
hello

(由 Yury Selivanov 在bpo-37028中貢獻。)

異常 asyncio.CancelledError 現在繼承自 BaseException 而不是 Exception,並且不再繼承自 concurrent.futures.CancelledError。(由 Yury Selivanov 在bpo-32528中貢獻。)

在 Windows 上,預設事件迴圈現在是 ProactorEventLoop。(由 Victor Stinner 在bpo-34687中貢獻。)

ProactorEventLoop 現在也支援 UDP。(由 Adam Meily 和 Andrew Svetlov 在bpo-29883中貢獻。)

ProactorEventLoop 現在可以被 KeyboardInterrupt (“CTRL+C”) 中斷。(由 Vladimir Matveev 在bpo-23057中貢獻。)

添加了 asyncio.Task.get_coro() 以獲取 asyncio.Task 中包裝的協程。(由 Alex Grönholm 在bpo-36999中貢獻。)

Asyncio 任務現在可以命名,可以透過將 name 關鍵字引數傳遞給 asyncio.create_task()create_task() 事件迴圈方法,或者透過在任務物件上呼叫 set_name() 方法。任務名稱在 asyncio.Taskrepr() 輸出中可見,也可以使用 get_name() 方法檢索。(由 Alex Grönholm 在bpo-34270中貢獻。)

增加了對 Happy Eyeballsasyncio.loop.create_connection() 的支援。為了指定行為,添加了兩個新引數:*happy_eyeballs_delay* 和 *interleave*。Happy Eyeballs 演算法透過嘗試同時使用 IPv4 和 IPv6 進行連線來提高支援它們的應用程式的響應能力。(由 twisteroid ambassador 在bpo-33530中貢獻。)

內建函式

compile() 內建函式已得到改進,可接受 ast.PyCF_ALLOW_TOP_LEVEL_AWAIT 標誌。透過傳遞此新標誌,compile() 將允許頂層 awaitasync forasync with 結構,這些結構通常被認為是無效語法。然後可以返回標有 CO_COROUTINE 標誌的非同步程式碼物件。(由 Matthias Bussonnier 在bpo-34616中貢獻)

collections

collections.namedtuple()_asdict() 方法現在返回一個 dict 而不是 collections.OrderedDict。這之所以有效,是因為自 Python 3.7 以來,常規字典保證了順序。如果需要 OrderedDict 的額外功能,建議的補救措施是將結果強制轉換為所需型別:OrderedDict(nt._asdict())。(由 Raymond Hettinger 在bpo-35864中貢獻。)

cProfile

cProfile.Profile 類現在可以用作上下文管理器。透過執行以下程式碼分析程式碼塊

import cProfile

with cProfile.Profile() as profiler:
      # code to be profiled
      ...

(由 Scott Sanderson 在bpo-29235中貢獻。)

csv

csv.DictReader 現在返回 dict 例項而不是 collections.OrderedDict。該工具現在更快,使用的記憶體更少,同時仍保留欄位順序。(由 Michael Selik 在bpo-34003中貢獻。)

curses

添加了一個新的變數,用於儲存底層 ncurses 庫的結構化版本資訊:ncurses_version。(由 Serhiy Storchaka 在bpo-31680中貢獻。)

ctypes

在 Windows 上,CDLL 及其子類現在接受 *winmode* 引數來指定底層 LoadLibraryEx 呼叫的標誌。預設標誌設定為僅從受信任的位置載入 DLL 依賴項,包括 DLL 儲存的路徑(如果使用完整或部分路徑載入初始 DLL)和透過 add_dll_directory() 新增的路徑。(由 Steve Dower 在bpo-36085中貢獻。)

datetime

添加了新的備用建構函式 datetime.date.fromisocalendar()datetime.datetime.fromisocalendar(),它們分別從 ISO 年、週數和工作日構造 datedatetime 物件;這些是每個類的 isocalendar 方法的逆操作。(由 Paul Ganssle 在bpo-36004中貢獻。)

functools

functools.lru_cache() 現在可以直接用作裝飾器,而不再是返回裝飾器的函式。因此,以下兩種方式現在都受支援

@lru_cache
def f(x):
    ...

@lru_cache(maxsize=256)
def f(x):
    ...

(由 Raymond Hettinger 在bpo-36772中貢獻。)

新增 functools.cached_property() 裝飾器,用於例項生命週期內快取的計算屬性。

import functools
import statistics

class Dataset:
   def __init__(self, sequence_of_numbers):
      self.data = sequence_of_numbers

   @functools.cached_property
   def variance(self):
      return statistics.variance(self.data)

(由 Carl Meyer 在bpo-21145中貢獻)

添加了一個新的 functools.singledispatchmethod() 裝飾器,它使用單一分派將方法轉換為泛型函式

from functools import singledispatchmethod
from contextlib import suppress

class TaskManager:

    def __init__(self, tasks):
        self.tasks = list(tasks)

    @singledispatchmethod
    def discard(self, value):
        with suppress(ValueError):
            self.tasks.remove(value)

    @discard.register(list)
    def _(self, tasks):
        targets = set(tasks)
        self.tasks = [x for x in self.tasks if x not in targets]

(由 Ethan Smith 在bpo-32380中貢獻)

gc

get_objects() 現在可以接收一個可選的 *generation* 引數,指示要獲取物件的代。(由 Pablo Galindo 在bpo-36016中貢獻。)

gettext

添加了 pgettext() 及其變體。(由 Franz Glasner、Éric Araujo 和 Cheryl Sabella 在bpo-2504中貢獻。)

gzip

gzip.compress() 添加了 *mtime* 引數,用於可重現的輸出。(由 Guo Ci Teo 在bpo-34898中貢獻。)

對於某些型別的無效或損壞的 gzip 檔案,現在會引發 BadGzipFile 異常,而不是 OSError。(由 Filip Gruszczyński、Michele Orrù 和 Zackery Spytz 在bpo-6584中貢獻。)

IDLE 和 idlelib

超出 N 行(預設為 50 行)的輸出會被壓縮成一個按鈕。N 可以在“設定”對話方塊的“通用”頁面的“PyShell”部分更改。透過右鍵單擊輸出,可以壓縮更少但可能過長的行。壓縮後的輸出可以透過雙擊按鈕在原地展開,或透過右鍵單擊按鈕展開到剪貼簿或單獨的視窗中。(由 Tal Einat 在bpo-1529353中貢獻。)

在“執行”選單中添加了“執行自定義”,以自定義設定執行模組。輸入的任何命令列引數都會新增到 sys.argv 中。它們也會在下次自定義執行的框中重新出現。還可以取消正常的 Shell 主模組重啟。(由 Cheryl Sabella、Terry Jan Reedy 和其他人在bpo-5680bpo-37627中貢獻。)

為 IDLE 編輯器視窗添加了可選的行號。除非在配置對話方塊的“常規”選項卡中另行設定,否則視窗在開啟時不會顯示行號。現有視窗的行號在“選項”選單中顯示和隱藏。(由 Tal Einat 和 Saimadhav Heblikar 在bpo-17535中貢獻。)

現在使用 OS 本機編碼在 Python 字串和 Tcl 物件之間進行轉換。這使得 IDLE 可以處理表情符號和其他非 BMP 字元。這些字元可以顯示或複製貼上到剪貼簿。從 Tcl 轉換為 Python 再轉換回 Python 現在永遠不會失敗。(許多人為此努力了八年,但 Serhiy Storchaka 最終在bpo-13153中解決了這個問題。)

3.8.1 中的新特性

添加了關閉游標閃爍的選項。(由 Zackery Spytz 在bpo-4603中貢獻。)

Esc 鍵現在關閉 IDLE 補全視窗。(由 Johnny Najera 在bpo-38944中貢獻。)

上述更改已回溯到 3.7 維護版本。

將關鍵字新增到模組名稱補全列表。(由 Terry J. Reedy 在bpo-37765中貢獻。)

inspect

inspect.getdoc() 函式現在可以為 __slots__ 查詢文件字串,如果該屬性是 dict,其中值是文件字串。這提供了與我們已經擁有的 property()classmethod()staticmethod() 類似的文件選項

class AudioClip:
    __slots__ = {'bit_rate': 'expressed in kilohertz to one decimal place',
                 'duration': 'in seconds, rounded up to an integer'}
    def __init__(self, bit_rate, duration):
        self.bit_rate = round(bit_rate / 1000.0, 1)
        self.duration = ceil(duration)

(由 Raymond Hettinger 在bpo-36326中貢獻。)

io

在開發模式 (-X env) 和除錯構建中,io.IOBase 終結器現在會記錄異常,如果 close() 方法失敗。在釋出構建中,異常預設會被靜默忽略。(由 Victor Stinner 在bpo-18748中貢獻。)

itertools

itertools.accumulate() 函式添加了一個可選的 *initial* 關鍵字引數來指定初始值

>>> from itertools import accumulate
>>> list(accumulate([10, 5, 30, 15], initial=1000))
[1000, 1010, 1015, 1045, 1060]

(由 Lisa Roach 在bpo-34659中貢獻。)

json.tool

新增選項 --json-lines 以將每個輸入行解析為單獨的 JSON 物件。(由 Weipeng Hong 在bpo-31553中貢獻。)

logging

logging.basicConfig() 添加了一個 *force* 關鍵字引數。當設定為 true 時,在執行其他引數指定的配置之前,會移除並關閉附加到根記錄器上的任何現有處理程式。

這解決了一個長期存在的問題。一旦呼叫了記錄器或 *basicConfig()*,對 *basicConfig()* 的後續呼叫就會被靜默忽略。這使得使用互動式提示符或 Jupyter Notebook 更新、試驗或教授各種日誌記錄配置選項變得困難。

(由 Raymond Hettinger 建議,由 Donghee Na 實現,由 Vinay Sajip 審查,在bpo-33897中。)

math

添加了新函式 math.dist(),用於計算兩點之間的歐幾里得距離。(由 Raymond Hettinger 在bpo-33089中貢獻。)

擴充套件了 math.hypot() 函式以處理多個維度。以前,它只支援二維情況。(由 Raymond Hettinger 在bpo-33089中貢獻。)

添加了新函式 math.prod(),作為 sum() 的類似函式,返回“起始”值(預設:1)乘以數字可迭代物件的乘積

>>> prior = 0.8
>>> likelihoods = [0.625, 0.84, 0.30]
>>> math.prod(likelihoods, start=prior)
0.126

(由 Pablo Galindo 在bpo-35606中貢獻。)

添加了兩個新的組合函式 math.perm()math.comb()

>>> math.perm(10, 3)    # Permutations of 10 things taken 3 at a time
720
>>> math.comb(10, 3)    # Combinations of 10 things taken 3 at a time
120

(由 Yash Aggarwal、Keller Fuchs、Serhiy Storchaka 和 Raymond Hettinger 在bpo-37128bpo-37178bpo-35431中貢獻。)

添加了一個新函式 math.isqrt(),用於計算精確的整數平方根,而無需轉換為浮點數。新函式支援任意大整數。它比 floor(sqrt(n)) 快,但比 math.sqrt()

>>> r = 650320427
>>> s = r ** 2
>>> isqrt(s - 1)         # correct
650320426
>>> floor(sqrt(s - 1))   # incorrect
650320427

(由 Mark Dickinson 在bpo-36887中貢獻。)

函式 math.factorial() 不再接受非整數類的引數。(由 Pablo Galindo 在bpo-33083中貢獻。)

mmap

mmap.mmap 類現在有一個 madvise() 方法來訪問 madvise() 系統呼叫。(由 Zackery Spytz 在bpo-32941中貢獻。)

multiprocessing

添加了新的 multiprocessing.shared_memory 模組。(由 Davin Potts 在bpo-35813中貢獻。)

在 macOS 上,預設使用 *spawn* 啟動方法。(由 Victor Stinner 在bpo-33725中貢獻。)

os

在 Windows 上,添加了新函式 add_dll_directory(),用於在匯入擴充套件模組或使用 ctypes 載入 DLL 時提供額外的本機依賴項搜尋路徑。(由 Steve Dower 在bpo-36085中貢獻。)

添加了一個新的 os.memfd_create() 函式來封裝 memfd_create() 系統呼叫。(由 Zackery Spytz 和 Christian Heimes 在bpo-26836中貢獻。)

在 Windows 上,大部分用於處理重新解析點(包括符號連結和目錄連線)的手動邏輯已委託給作業系統。具體來說,os.stat() 現在將遍歷作業系統支援的任何內容,而 os.lstat() 只會開啟標識為“名稱代理”的重新解析點,而其他重新解析點則像 os.stat() 一樣開啟。在所有情況下,os.stat_result.st_mode 將只為符號連結設定 S_IFLNK,而不為其他型別的重新解析點設定。要識別其他型別的重新解析點,請檢查新的 os.stat_result.st_reparse_tag 屬性。

在 Windows 上,os.readlink() 現在能夠讀取目錄連線。請注意,islink() 對於目錄連線將返回 False,因此首先檢查 islink 的程式碼將繼續將連線視為目錄,而處理來自 os.readlink() 的錯誤的程式碼現在可能會將連線視為連結。

(由 Steve Dower 在bpo-37834中貢獻。)

os.path

os.path 函式(例如 exists()lexists()isdir()isfile()islink()ismount() 等返回布林結果的函式)現在返回 False,而不是對於包含作業系統級別不可表示字元或位元組的路徑引發 ValueError 或其子類 UnicodeEncodeErrorUnicodeDecodeError。(由 Serhiy Storchaka 在bpo-33721中貢獻。)

在 Windows 上,expanduser() 現在優先使用 USERPROFILE 環境變數,並且不使用 HOME,後者通常未為普通使用者帳戶設定。(由 Anthony Sottile 在bpo-36264中貢獻。)

在 Windows 上,isdir() 不再對指向不存在目錄的連結返回 True

在 Windows 上,realpath() 現在解析重新解析點,包括符號連結和目錄連線。

(由 Steve Dower 在bpo-37834中貢獻。)

pathlib

pathlib.Path 返回布林結果的方法(如 exists()is_dir()is_file()is_mount()is_symlink()is_block_device()is_char_device()is_fifo()is_socket())現在返回 False,而不是對於包含作業系統級別不可表示字元的路徑引發 ValueError 或其子類 UnicodeEncodeError。(由 Serhiy Storchaka 在bpo-33721中貢獻。)

添加了 pathlib.Path.link_to(),它建立一個指向路徑的硬連結。(由 Joannah Nanjekye 在bpo-26978中貢獻)請注意,link_to 在 3.10 中被棄用,並在 3.12 中被刪除,取而代之的是 3.10 中新增的 hardlink_to 方法,它與現有 symlink_to 方法的語義匹配。

pickle

子類化 C 最佳化 Picklerpickle 擴充套件現在可以透過定義特殊的 reducer_override() 方法來重寫函式和類的 pickle 邏輯。(由 Pierre Glaser 和 Olivier Grisel 在bpo-35900中貢獻。)

plistlib

添加了新的 plistlib.UID 並啟用了對讀取和寫入 NSKeyedArchiver 編碼的二進位制 plist 的支援。(由 Jon Janzen 在bpo-26707中貢獻。)

pprint

pprint 模組為幾個函式添加了 *sort_dicts* 引數。預設情況下,這些函式在渲染或列印之前繼續對字典進行排序。但是,如果將 *sort_dicts* 設定為 false,字典將保留鍵的插入順序。這在除錯期間與 JSON 輸入進行比較時很有用。

此外,還新增了一個便捷功能 pprint.pp(),它類似於 pprint.pprint(),但預設將 sort_dicts 設定為 False

>>> from pprint import pprint, pp
>>> d = dict(source='input.txt', operation='filter', destination='output.txt')
>>> pp(d, width=40)                  # Original order
{'source': 'input.txt',
 'operation': 'filter',
 'destination': 'output.txt'}
>>> pprint(d, width=40)              # Keys sorted alphabetically
{'destination': 'output.txt',
 'operation': 'filter',
 'source': 'input.txt'}

(由 Rémi Lapeyre 在 bpo-30670 中貢獻。)

py_compile

py_compile.compile() 現在支援靜默模式。(由 Joannah Nanjekye 在 bpo-22640 中貢獻。)

shlex

新的 shlex.join() 函式作為 shlex.split() 的逆操作。(由 Bo Bayles 在 bpo-32102 中貢獻。)

shutil

shutil.copytree() 現在接受一個新的 dirs_exist_ok 關鍵字引數。(由 Josh Bronson 在 bpo-20849 中貢獻。)

shutil.make_archive() 現在預設使用現代的 pax (POSIX.1-2001) 格式建立新檔案,以提高可移植性和符合標準,這繼承自 tarfile 模組的相應更改。(由 C.A.M. Gerlach 在 bpo-30661 中貢獻。)

shutil.rmtree() 在 Windows 上現在會直接刪除目錄連線點,而不會先遞迴刪除其內容。(由 Steve Dower 在 bpo-37834 中貢獻。)

socket

添加了 create_server()has_dualstack_ipv6() 便捷函式,用於自動化建立伺服器套接字時通常涉及的必要任務,包括在同一套接字上接受 IPv4 和 IPv6 連線。(由 Giampaolo Rodolà 在 bpo-17561 中貢獻。)

socket.if_nameindex(), socket.if_nametoindex(), 和 socket.if_indextoname() 函式已在 Windows 上實現。(由 Zackery Spytz 在 bpo-37007 中貢獻。)

ssl

添加了 post_handshake_auth 以啟用,並添加了 verify_client_post_handshake() 以啟動 TLS 1.3 握手後認證。(由 Christian Heimes 在 bpo-34670 中貢獻。)

statistics

添加了 statistics.fmean() 作為 statistics.mean() 的更快浮點變體。(由 Raymond Hettinger 和 Steven D’Aprano 在 bpo-35904 中貢獻。)

添加了 statistics.geometric_mean() (由 Raymond Hettinger 在 bpo-27181 中貢獻。)

添加了 statistics.multimode(),它返回最常見值的列表。(由 Raymond Hettinger 在 bpo-35892 中貢獻。)

添加了 statistics.quantiles(),它將資料或分佈劃分為等機率區間(例如四分位數、十分位數或百分位數)。(由 Raymond Hettinger 在 bpo-36546 中貢獻。)

添加了 statistics.NormalDist,這是一個用於建立和操縱隨機變數正態分佈的工具。(由 Raymond Hettinger 在 bpo-36018 中貢獻。)

>>> temperature_feb = NormalDist.from_samples([4, 12, -3, 2, 7, 14])
>>> temperature_feb.mean
6.0
>>> temperature_feb.stdev
6.356099432828281

>>> temperature_feb.cdf(3)            # Chance of being under 3 degrees
0.3184678262814532
>>> # Relative chance of being 7 degrees versus 10 degrees
>>> temperature_feb.pdf(7) / temperature_feb.pdf(10)
1.2039930378537762

>>> el_niño = NormalDist(4, 2.5)
>>> temperature_feb += el_niño        # Add in a climate effect
>>> temperature_feb
NormalDist(mu=10.0, sigma=6.830080526611674)

>>> temperature_feb * (9/5) + 32      # Convert to Fahrenheit
NormalDist(mu=50.0, sigma=12.294144947901014)
>>> temperature_feb.samples(3)        # Generate random samples
[7.672102882379219, 12.000027119750287, 4.647488369766392]

sys

添加了新的 sys.unraisablehook() 函式,可以重寫該函式來控制如何處理“無法引發的異常”。當發生異常但 Python 無法處理時,例如在解構函式引發異常或垃圾回收 (gc.collect()) 期間,就會呼叫它。(由 Victor Stinner 在 bpo-36829 中貢獻。)

tarfile

tarfile 模組現在預設使用現代的 pax (POSIX.1-2001) 格式建立新檔案,而不是以前的 GNU 特有格式。這透過標準化和可擴充套件格式中的一致編碼 (UTF-8) 提高了跨平臺可移植性,並提供了其他多項優點。(由 C.A.M. Gerlach 在 bpo-36268 中貢獻。)

threading

添加了一個新的 threading.excepthook() 函式,用於處理未捕獲的 threading.Thread.run() 異常。它可以被覆蓋以控制如何處理未捕獲的 threading.Thread.run() 異常。(由 Victor Stinner 在 bpo-1230540 中貢獻。)

threading.Thread 類添加了一個新的 threading.get_native_id() 函式和一個 native_id 屬性。這些返回由核心分配的當前執行緒的本地整數執行緒 ID。此功能僅在某些平臺可用,詳情請參見 get_native_id。(由 Jake Tesler 在 bpo-36084 中貢獻。)

tokenize

tokenize 模組現在在提供的輸入沒有尾隨換行符時,會隱式發出一個 NEWLINE 標記。此行為現在與 C 詞法分析器內部的行為一致。(由 Ammar Askar 在 bpo-33899 中貢獻。)

tkinter

tkinter.Spinbox 類中添加了方法 selection_from(), selection_present(), selection_range()selection_to()。(由 Juliette Monsel 在 bpo-34829 中貢獻。)

tkinter.Canvas 類中添加了方法 moveto()。(由 Juliette Monsel 在 bpo-23831 中貢獻。)

tkinter.PhotoImage 類現在具有 transparency_get()transparency_set() 方法。(由 Zackery Spytz 在 bpo-25451 中貢獻。)

時間

為 macOS 10.12 添加了新的時鐘 CLOCK_UPTIME_RAW。(由 Joannah Nanjekye 在 bpo-35702 中貢獻。)

typing

typing 模組包含幾個新功能

unicodedata

unicodedata 模組已升級,以使用 Unicode 12.1.0 版本。

新的函式 is_normalized() 可用於驗證字串是否處於特定正規化,通常比實際規範化字串快得多。(由 Max Belanger、David Euresti 和 Greg Price 在 bpo-32285bpo-37966 中貢獻)。

unittest

添加了 AsyncMock 以支援 Mock 的非同步版本。還添加了適當的新斷言函式用於測試。(由 Lisa Roach 在 bpo-26467 中貢獻)。

在 unittest 中添加了 addModuleCleanup()addClassCleanup() 以支援 setUpModule()setUpClass() 的清理。(由 Lisa Roach 在 bpo-24412 中貢獻。)

幾個 mock 斷言函式在失敗時也會列印實際呼叫列表。(由 Petter Strandmark 在 bpo-35047 中貢獻。)

unittest 模組獲得了對協程作為測試用例與 unittest.IsolatedAsyncioTestCase 一起使用的支援。(由 Andrew Svetlov 在 bpo-32972 中貢獻。)

示例

import unittest


class TestRequest(unittest.IsolatedAsyncioTestCase):

    async def asyncSetUp(self):
        self.connection = await AsyncConnection()

    async def test_get(self):
        response = await self.connection.get("https://example.com")
        self.assertEqual(response.status_code, 200)

    async def asyncTearDown(self):
        await self.connection.close()


if __name__ == "__main__":
    unittest.main()

venv

venv 現在在所有平臺上都包含一個 Activate.ps1 指令碼,用於在 PowerShell Core 6.1 下啟用虛擬環境。(由 Brett Cannon 在 bpo-32718 中貢獻。)

weakref

weakref.proxy() 返回的代理物件現在除了其他數字運算子外,還支援矩陣乘法運算子 @@=。(由 Mark Dickinson 在 bpo-36669 中貢獻。)

xml

作為對 DTD 和外部實體檢索的緩解措施,xml.dom.minidomxml.sax 模組預設不再處理外部實體。(由 Christian Heimes 在 bpo-17239 中貢獻。)

xml.etree.ElementTree 模組中的 .find*() 方法支援萬用字元搜尋,例如 {*}tag 會忽略名稱空間,{namespace}* 會返回給定名稱空間中的所有標籤。(由 Stefan Behnel 在 bpo-28238 中貢獻。)

xml.etree.ElementTree 模組提供了一個新函式 canonicalize(),它實現了 C14N 2.0。(由 Stefan Behnel 在 bpo-13611 中貢獻。)

xml.etree.ElementTree.XMLParser 的目標物件可以透過新的回撥方法 start_ns()end_ns() 接收名稱空間宣告事件。此外,可以配置 xml.etree.ElementTree.TreeBuilder 目標以處理有關注釋和處理指令的事件,以將它們包含在生成的樹中。(由 Stefan Behnel 在 bpo-36676bpo-36673 中貢獻。)

xmlrpc

xmlrpc.client.ServerProxy 現在支援可選的 headers 關鍵字引數,用於在每個請求中傳送一系列 HTTP 標頭。除此之外,這使得從預設基本認證升級到更快的會話認證成為可能。(由 Cédric Krier 在 bpo-35153 中貢獻。)

最佳化

  • subprocess 模組現在在某些情況下可以使用 os.posix_spawn() 函式以獲得更好的效能。目前,它僅在 macOS 和 Linux(使用 glibc 2.24 或更高版本)上使用,如果滿足所有這些條件:

    • close_fds 為假;

    • 未設定 preexec_fnpass_fdscwdstart_new_session 引數;

    • executable 路徑包含一個目錄。

    (由 Joannah Nanjekye 和 Victor Stinner 在 bpo-35537 中貢獻。)

  • shutil.copyfile()shutil.copy()shutil.copy2()shutil.copytree()shutil.move() 在 Linux 和 macOS 上使用平臺特定的“快速複製”系統呼叫,以更高效地複製檔案。“快速複製”意味著複製操作發生在核心內部,避免了像“outfd.write(infd.read())”那樣在 Python 中使用使用者空間緩衝區。在 Windows 上,shutil.copyfile() 使用更大的預設緩衝區大小(1 MiB 而不是 16 KiB),並使用基於 memoryview()shutil.copyfileobj() 變體。在同一分割槽內複製 512 MiB 檔案的速度在 Linux 上提高了約 +26%,在 macOS 上提高了 +50%,在 Windows 上提高了 +40%。此外,消耗的 CPU 週期也大大減少。參見 平臺相關高效複製操作 部分。(由 Giampaolo Rodolà 在 bpo-33671 中貢獻。)

  • shutil.copytree() 使用 os.scandir() 函式,所有依賴它的複製函式都使用快取的 os.stat() 值。複製包含 8000 個檔案的目錄的速度在 Linux 上提高了約 +9%,在 Windows 上提高了 +20%,在 Windows SMB 共享上提高了 +30%。此外,os.stat() 系統呼叫的數量減少了 38%,使得 shutil.copytree() 在網路檔案系統上特別快。(由 Giampaolo Rodolà 在 bpo-33695 中貢獻。)

  • pickle 模組中的預設協議現在是協議 4,該協議首次在 Python 3.4 中引入。與 Python 3.0 中提供的協議 3 相比,它提供了更好的效能和更小的尺寸。

  • PyGC_Head 中刪除了一個 Py_ssize_t 成員。所有 GC 跟蹤物件(例如元組、列表、字典)的大小都減少了 4 或 8 位元組。(由 Inada Naoki 在 bpo-33597 中貢獻。)

  • uuid.UUID 現在使用 __slots__ 來減少其記憶體佔用。(由 Wouter Bolsterlee 和 Tal Einat 在 bpo-30977 中貢獻)

  • operator.itemgetter() 的效能提高了 33%。優化了引數處理,併為元組中單個非負整數索引的常見情況(這是標準庫中的典型用例)添加了快速路徑。(由 Raymond Hettinger 在 bpo-35664 中貢獻。)

  • 加快了 collections.namedtuple() 中的欄位查詢。它們現在速度快了兩倍以上,使其成為 Python 中最快的例項變數查詢形式。(由 Raymond Hettinger, Pablo Galindo, Joe Jevnik, Serhiy Storchaka 在 bpo-32492 中貢獻。)

  • 如果輸入可迭代物件具有已知長度(輸入實現了 __len__),則 list 建構函式不會過度分配內部專案緩衝區。這使得建立的列表平均小 12%。(由 Raymond Hettinger 和 Pablo Galindo 在 bpo-33234 中貢獻。)

  • 類變數寫入速度翻倍。當更新非雙下劃線屬性時,會不必要地呼叫更新槽。(由 Stefan Behnel、Pablo Galindo Salgado、Raymond Hettinger、Neil Schemenauer 和 Serhiy Storchaka 在 bpo-36012 中貢獻。)

  • 減少了許多內建函式和方法引數轉換的開銷。這使得呼叫一些簡單的內建函式和方法的速度提高了 20-50%。(由 Serhiy Storchaka 在 bpo-23867bpo-35582bpo-36127 中貢獻。)

  • LOAD_GLOBAL 指令現在使用新的“逐操作碼快取”機制。它現在快了大約 40%。(由 Yury Selivanov 和 Inada Naoki 在 bpo-26219 中貢獻。)

構建和 C API 更改

  • 預設 sys.abiflags 變為空字串:用於 pymalloc 的 m 標誌變得無用(使用和不使用 pymalloc 的構建都是 ABI 相容的),因此已被移除。(由 Victor Stinner 在 bpo-36707 中貢獻。)

    更改示例

    • 只安裝 python3.8 程式,python3.8m 程式已移除。

    • 只安裝 python3.8-config 指令碼,python3.8m-config 指令碼已移除。

    • 動態庫檔名的字尾中已移除 m 標誌:標準庫中的擴充套件模組以及由第三方包(例如從 PyPI 下載的包)生成和安裝的擴充套件模組。例如,在 Linux 上,Python 3.7 的字尾 .cpython-37m-x86_64-linux-gnu.so 在 Python 3.8 中變為 .cpython-38-x86_64-linux-gnu.so

  • 標頭檔案已重新組織,以更好地分離不同種類的 API

    • Include/*.h 應該是可移植的公共穩定 C API。

    • Include/cpython/*.h 應該是特定於 CPython 的不穩定 C API;公共 API,其中一些私有 API 以 _Py_PY 為字首。

    • Include/internal/*.h 是特定於 CPython 的私有內部 C API。此 API 不提供向後相容性保證,不應在 CPython 之外使用。它僅適用於非常特殊的需求,例如偵錯程式和分析器,它們必須訪問 CPython 內部而無需呼叫函式。此 API 現在由 make install 安裝。

    (由 Victor Stinner 在 bpo-35134bpo-35081 中貢獻,Eric Snow 在 Python 3.7 中啟動了這項工作。)

  • 一些宏已被轉換為靜態行內函數:引數型別和返回型別明確定義,它們沒有宏特有的問題,變數具有區域性作用域。示例

    (由 Victor Stinner 在 bpo-35059 中貢獻。)

  • PyByteArray_Init()PyByteArray_Fini() 函式已被移除。自 Python 2.7.4 和 Python 3.2.0 以來,它們什麼也沒做,被排除在有限的 API(穩定 ABI)之外,並且沒有被記錄。(由 Victor Stinner 在 bpo-35713 中貢獻。)

  • PyExceptionClass_Name() 的結果現在是 const char * 型別,而不是 char * 型別。(由 Serhiy Storchaka 在 bpo-33818 中貢獻。)

  • Modules/Setup.distModules/Setup 的二元性已被移除。以前,在更新 CPython 原始碼樹時,為了反映任何上游更改,必須手動將 Modules/Setup.dist(在原始碼樹內)複製到 Modules/Setup(在構建樹內)。這給打包人員帶來了微不足道的便利,卻給跟蹤 CPython 開發的開發人員帶來了頻繁的煩惱,因為忘記複製檔案可能會導致構建失敗。

    現在構建系統始終從原始碼樹中的 Modules/Setup 讀取。希望自定義該檔案的人被鼓勵將他們的更改保留在 CPython 的 git 分支中或作為補丁檔案,就像他們對原始碼樹的任何其他更改所做的那樣。

    (由 Antoine Pitrou 在 bpo-32430 中貢獻。)

  • 將 Python 數字轉換為 C 整數的函式,例如 PyLong_AsLong(),以及帶整數轉換格式單元(如 'i')的引數解析函式,例如 PyArg_ParseTuple(),現在將使用 __index__() 特殊方法而不是 __int__()(如果可用)。對於具有 __int__() 方法但沒有 __index__() 方法的物件(如 DecimalFraction),將發出棄用警告。PyNumber_Check() 現在將為實現 __index__() 的物件返回 1PyNumber_Long()PyNumber_Float()PyFloat_AsDouble() 現在也使用 __index__() 方法(如果可用)。(由 Serhiy Storchaka 在 bpo-36048bpo-20092 中貢獻。)

  • 堆分配的型別物件現在將在 PyObject_Init() (及其並行宏 PyObject_INIT) 中增加其引用計數,而不是在 PyType_GenericAlloc() 中。修改例項分配或解除分配的型別可能需要進行調整。(由 Eddie Elizondo 在 bpo-35810 中貢獻。)

  • 新函式 PyCode_NewWithPosOnlyArgs() 允許像 PyCode_New() 一樣建立程式碼物件,但帶有一個額外的 posonlyargcount 引數,用於指示僅限位置引數的數量。(由 Pablo Galindo 在 bpo-37221 中貢獻。)

  • Py_SetPath() 現在將 sys.executable 設定為程式的完整路徑 (Py_GetProgramFullPath()) 而不是程式名稱 (Py_GetProgramName())。(由 Victor Stinner 在 bpo-38234 中貢獻。)

已棄用

API 和功能移除

以下功能和 API 已從 Python 3.8 中移除

  • 從 Python 3.3 開始,從 collections 匯入 ABC 已被棄用,應從 collections.abc 匯入。從 collections 匯入的功能原定於 3.8 中移除,但已推遲到 3.9。(參見 gh-81134。)

  • 在 Python 3.7 中已棄用的 macpath 模組已移除。(由 Victor Stinner 在 bpo-35471 中貢獻。)

  • 函式 platform.popen() 已移除,自 Python 3.3 起已被棄用:請改用 os.popen()。(由 Victor Stinner 在 bpo-35345 中貢獻。)

  • 函式 time.clock() 已被移除,自 Python 3.3 起已被棄用:請根據您的要求改用 time.perf_counter()time.process_time(),以獲得明確定義的行為。(由 Matthias Bussonnier 在 bpo-36895 中貢獻。)

  • 已移除 pyvenv 指令碼,轉而使用 python3.8 -m venv,以幫助消除關於 pyvenv 指令碼與哪個 Python 直譯器繫結的困惑。(由 Brett Cannon 在 bpo-25427 中貢獻。)

  • parse_qsparse_qslescape 已從 cgi 模組中移除。它們在 Python 3.2 或更早版本中已棄用。應從 urllib.parsehtml 模組中匯入它們。

  • filemode 函式已從 tarfile 模組中移除。它沒有被文件記錄,並且自 Python 3.3 以來已被棄用。

  • XMLParser 建構函式不再接受 html 引數。它從未生效,並且在 Python 3.4 中已被棄用。所有其他引數現在都是 僅限關鍵字引數。(由 Serhiy Storchaka 在 bpo-29209 中貢獻。)

  • 已移除 XMLParserdoctype() 方法。(由 Serhiy Storchaka 在 bpo-29209 中貢獻。)

  • “unicode_internal” 編解碼器已移除。(由 Inada Naoki 在 bpo-36297 中貢獻。)

  • sqlite3 模組的 CacheStatement 物件不會暴露給使用者。(由 Aviv Palivoda 在 bpo-30262 中貢獻。)

  • fileinput.input()fileinput.FileInput()bufsize 關鍵字引數已被移除,該引數自 Python 3.6 以來已被忽略和棄用。bpo-36952 (由 Matthias Bussonnier 貢獻。)

  • 在 Python 3.7 中已棄用的函式 sys.set_coroutine_wrapper()sys.get_coroutine_wrapper() 已移除;bpo-36933 (由 Matthias Bussonnier 貢獻。)

遷移到 Python 3.8

本節列出了前面描述過的變更以及其他可能需要修改程式碼的 bug 修復。

Python 行為的變更

  • 生成器表示式和推導式(除了最左側 for 子句中的可迭代表達式)現在禁止使用 yield 表示式(包括 yieldyield from 子句)。(由 Serhiy Storchaka 在 bpo-10544 中貢獻。)

  • 當身份檢查(isis not)與某些型別的字面量(例如字串、數字)一起使用時,編譯器現在會生成 SyntaxWarning。這些在 CPython 中通常會偶然工作,但語言規範不保證。該警告建議使用者改用相等性測試(==!=)。(由 Serhiy Storchaka 在 bpo-34850 中貢獻。)

  • CPython 直譯器在某些情況下可能會吞噬異常。在 Python 3.8 中,這種情況發生得更少。特別是,從型別字典獲取屬性時引發的異常不再被忽略。(由 Serhiy Storchaka 在 bpo-35459 中貢獻。)

  • 移除了內建型別 boolintfloatcomplex 以及標準庫中少數幾個類的 __str__ 實現。它們現在繼承了 object__str__()。因此,在這些類的子類中定義 __repr__() 方法將影響它們的字串表示。(由 Serhiy Storchaka 在 bpo-36793 中貢獻。)

  • 在 AIX 上,sys.platform 不再包含主版本號。它始終是 'aix',而不是 'aix3' .. 'aix7'。由於較舊的 Python 版本包含版本號,因此建議始終使用 sys.platform.startswith('aix')。(由 M. Felt 在 bpo-36588 中貢獻。)

  • PyEval_AcquireLock()PyEval_AcquireThread() 現在在直譯器正在終止時被呼叫會終止當前執行緒,使其與 PyEval_RestoreThread()Py_END_ALLOW_THREADS()PyGILState_Ensure() 保持一致。如果不需要此行為,請透過檢查 _Py_IsFinalizing()sys.is_finalizing() 來防護呼叫。(由 Joannah Nanjekye 在 bpo-36475 中貢獻。)

Python API 的變化

  • 在 Windows 上,擴充套件模組和用 ctypes 載入的 DLL 的 DLL 依賴項現在解析更安全。只有系統路徑、包含 DLL 或 PYD 檔案的目錄以及透過 add_dll_directory() 新增的目錄才會被搜尋以查詢載入時依賴項。具體來說,PATH 和當前工作目錄不再使用,並且對它們的修改將不再對正常的 DLL 解析產生任何影響。如果您的應用程式依賴於這些機制,您應該檢查 add_dll_directory(),如果它存在,則在載入庫時使用它來新增您的 DLL 目錄。請注意,Windows 7 使用者需要確保已安裝 Windows 更新 KB2533623(安裝程式也會驗證這一點)。(由 Steve Dower 在 bpo-36085 中貢獻。)

  • 在 pgen 被純 Python 實現取代後,相關的標頭檔案和函式已被刪除。(由 Pablo Galindo 在 bpo-36623 中貢獻。)

  • types.CodeType 在建構函式的第二個位置(posonlyargcount)有一個新引數,以支援 PEP 570 中定義的僅限位置引數。第一個引數(argcount)現在表示位置引數的總數(包括僅限位置引數)。types.CodeType 的新 replace() 方法可用於使程式碼面向未來。

  • hmac.new() 的引數 digestmod 不再預設使用 MD5 摘要。

C API 的變化

  • PyCompilerFlags 結構有一個新的 cf_feature_version 欄位。它應初始化為 PY_MINOR_VERSION。此欄位預設被忽略,並且僅當 cf_flags 中設定了 PyCF_ONLY_AST 標誌時才使用。(由 Guido van Rossum 在 bpo-35766 中貢獻。)

  • PyEval_ReInitThreads() 函式已從 C API 中刪除。它不應被顯式呼叫:請改用 PyOS_AfterFork_Child()。(由 Victor Stinner 在 bpo-36728 中貢獻。)

  • 在 Unix 上,C 擴充套件不再連結到 libpython,除了 Android 和 Cygwin。當 Python 被嵌入時,libpython 不得使用 RTLD_LOCAL 載入,而應使用 RTLD_GLOBAL。以前,使用 RTLD_LOCAL 時,已經無法載入未連結到 libpython 的 C 擴充套件,例如由 Modules/Setup*shared* 部分構建的標準庫 C 擴充套件。(由 Victor Stinner 在 bpo-21536 中貢獻。)

  • 在未定義 PY_SSIZE_T_CLEAN 的情況下,在解析或構建值時使用格式的 # 變體(例如 PyArg_ParseTuple()Py_BuildValue()PyObject_CallFunction() 等)現在會引發 DeprecationWarning。它將在 3.10 或 4.0 中刪除。詳情請閱讀 解析引數和構建值。(由 Inada Naoki 在 bpo-36381 中貢獻。)

  • 堆分配型別(例如使用 PyType_FromSpec() 建立的型別)的例項持有對其型別物件的引用。這些型別物件的引用計數增加已從 PyType_GenericAlloc() 移至更低階的函式 PyObject_Init()PyObject_INIT。這使得透過 PyType_FromSpec() 建立的型別在託管程式碼中與其他類一樣。

    靜態分配的型別 不受影響。

    在絕大多數情況下,應該沒有副作用。然而,手動在分配例項後增加引用計數(可能為了解決錯誤)的型別現在可能會變成永久性。為了避免這種情況,這些類需要在例項釋放期間呼叫 Py_DECREF 型別物件。

    為了將這些型別正確移植到 3.8,請應用以下更改:

    • 在分配例項後(如果有的話),刪除型別物件上的 Py_INCREF。這可能發生在呼叫 PyObject_NewPyObject_NewVarPyObject_GC_New()PyObject_GC_NewVar() 或任何其他使用 PyObject_Init()PyObject_INIT 的自定義分配器之後。

      示例

      static foo_struct *
      foo_new(PyObject *type) {
          foo_struct *foo = PyObject_GC_New(foo_struct, (PyTypeObject *) type);
          if (foo == NULL)
              return NULL;
      #if PY_VERSION_HEX < 0x03080000
          // Workaround for Python issue 35810; no longer necessary in Python 3.8
          PY_INCREF(type)
      #endif
          return foo;
      }
      
    • 確保堆分配型別的自定義 tp_dealloc 函式會減少型別的引用計數。

      示例

      static void
      foo_dealloc(foo_struct *instance) {
          PyObject *type = Py_TYPE(instance);
          PyObject_GC_Del(instance);
      #if PY_VERSION_HEX >= 0x03080000
          // This was not needed before Python 3.8 (Python issue 35810)
          Py_DECREF(type);
      #endif
      }
      

    (由 Eddie Elizondo 在 bpo-35810 中貢獻。)

  • Py_DEPRECATED() 宏已為 MSVC 實現。該宏現在必須放置在符號名稱之前。

    示例

    Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);
    

    (由 Zackery Spytz 在 bpo-33407 中貢獻。)

  • 直譯器不再假裝支援擴充套件型別在功能版本之間的二進位制相容性。PyTypeObject 由第三方擴充套件模組匯出時,應該具有當前 Python 版本中預期的所有槽,包括 tp_finalize(不再檢查 Py_TPFLAGS_HAVE_FINALIZE,然後再讀取 tp_finalize)。

    (由 Antoine Pitrou 在 bpo-32388 中貢獻。)

  • 函式 PyNode_AddChild()PyParser_AddToken() 現在接受兩個額外的 int 引數 end_linenoend_col_offset

  • libpython38.a 檔案(允許 MinGW 工具直接連結到 python38.dll)不再包含在常規 Windows 分發中。如果需要此檔案,可以使用 MinGW binutils 包中的 gendefdlltool 工具生成。

    gendef - python38.dll > tmp.def
    dlltool --dllname python38.dll --def tmp.def --output-lib libpython38.a
    

    已安裝的 pythonXY.dll 的位置將取決於安裝選項以及 Windows 的版本和語言。有關更多資訊,請參閱 在 Windows 上使用 Python。生成的庫應放置在與 pythonXY.lib 相同的目錄中,通常是 Python 安裝目錄下的 libs 目錄。

    (由 Steve Dower 在 bpo-37351 中貢獻。)

CPython 位元組碼變更

  • 直譯器迴圈透過將塊堆疊展開的邏輯移到編譯器中而得到了簡化。編譯器現在發出明確的指令,用於調整值堆疊並呼叫 breakcontinuereturn 的清理程式碼。

    刪除了操作碼 BREAK_LOOPCONTINUE_LOOPSETUP_LOOPSETUP_EXCEPT。添加了新操作碼 ROT_FOURBEGIN_FINALLYCALL_FINALLYPOP_FINALLY。更改了 END_FINALLYWITH_CLEANUP_START 的行為。

    (由 Mark Shannon、Antoine Pitrou 和 Serhiy Storchaka 在 bpo-17611 中貢獻。)

  • 添加了新的操作碼 END_ASYNC_FOR,用於處理在 async for 迴圈中等待下一個項時引發的異常。(由 Serhiy Storchaka 在 bpo-33041 中貢獻。)

  • MAP_ADD 現在期望值作為堆疊中的第一個元素,鍵作為第二個元素。進行此更改是為了使鍵在字典推導式中始終在值之前進行評估,如 PEP 572 所提議的。(由 Jörn Heissler 在 bpo-35224 中貢獻。)

演示和工具

添加了一個用於計時各種變數訪問方式的基準指令碼:Tools/scripts/var_access_benchmark.py。(由 Raymond Hettinger 在 bpo-35884 中貢獻。)

以下是自 Python 3.3 以來效能改進的總結

Python version                       3.3     3.4     3.5     3.6     3.7     3.8
--------------                       ---     ---     ---     ---     ---     ---

Variable and attribute read access:
    read_local                       4.0     7.1     7.1     5.4     5.1     3.9
    read_nonlocal                    5.3     7.1     8.1     5.8     5.4     4.4
    read_global                     13.3    15.5    19.0    14.3    13.6     7.6
    read_builtin                    20.0    21.1    21.6    18.5    19.0     7.5
    read_classvar_from_class        20.5    25.6    26.5    20.7    19.5    18.4
    read_classvar_from_instance     18.5    22.8    23.5    18.8    17.1    16.4
    read_instancevar                26.8    32.4    33.1    28.0    26.3    25.4
    read_instancevar_slots          23.7    27.8    31.3    20.8    20.8    20.2
    read_namedtuple                 68.5    73.8    57.5    45.0    46.8    18.4
    read_boundmethod                29.8    37.6    37.9    29.6    26.9    27.7

Variable and attribute write access:
    write_local                      4.6     8.7     9.3     5.5     5.3     4.3
    write_nonlocal                   7.3    10.5    11.1     5.6     5.5     4.7
    write_global                    15.9    19.7    21.2    18.0    18.0    15.8
    write_classvar                  81.9    92.9    96.0   104.6   102.1    39.2
    write_instancevar               36.4    44.6    45.8    40.0    38.9    35.5
    write_instancevar_slots         28.7    35.6    36.1    27.3    26.6    25.7

Data structure read access:
    read_list                       19.2    24.2    24.5    20.8    20.8    19.0
    read_deque                      19.9    24.7    25.5    20.2    20.6    19.8
    read_dict                       19.7    24.3    25.7    22.3    23.0    21.0
    read_strdict                    17.9    22.6    24.3    19.5    21.2    18.9

Data structure write access:
    write_list                      21.2    27.1    28.5    22.5    21.6    20.0
    write_deque                     23.8    28.7    30.1    22.7    21.8    23.5
    write_dict                      25.9    31.4    33.3    29.3    29.2    24.7
    write_strdict                   22.9    28.4    29.9    27.5    25.2    23.1

Stack (or queue) operations:
    list_append_pop                144.2    93.4   112.7    75.4    74.2    50.8
    deque_append_pop                30.4    43.5    57.0    49.4    49.2    42.5
    deque_append_popleft            30.8    43.7    57.3    49.7    49.7    42.8

Timing loop:
    loop_overhead                    0.3     0.5     0.6     0.4     0.3     0.3

這些基準測試是在執行 macOS 64 位構建的 Intel® Core™ i7-4960HQ 處理器 上測量的,可在 python.org 找到。基準指令碼以納秒顯示計時。

Python 3.8.1 的顯著變化

由於重大的安全問題,不再支援 asyncio.loop.create_datagram_endpoint()reuse_address 引數。這是由於 SO_REUSEADDR 套接字選項在 UDP 中的行為。有關更多詳細資訊,請參見 loop.create_datagram_endpoint() 的文件。(由 Kyle Stanley、Antoine Pitrou 和 Yury Selivanov 在 bpo-37228 中貢獻。)

Python 3.8.2 的顯著變化

修復了 shutil.copytree()ignore 回撥中的迴歸。引數型別現在又變回 str 和 List[str]。(由 Manuel Barkhau 和 Giampaolo Rodola 在 gh-83571 中貢獻。)

Python 3.8.3 的顯著變化

為了防止與編譯器標誌衝突,__future__ 模組中未來標誌的常量值已更新。以前 PyCF_ALLOW_TOP_LEVEL_AWAITCO_FUTURE_DIVISION 衝突。(由 Batuhan Taskaya 在 gh-83743 中貢獻。)

Python 3.8.8 的顯著變化

早期 Python 版本允許在 urllib.parse.parse_qs()urllib.parse.parse_qsl() 中同時使用 ;& 作為查詢引數分隔符。出於安全考慮,併為了符合最新的 W3C 建議,這已更改為只允許使用單個分隔符鍵,預設值為 &。此更改也影響 cgi.parse()cgi.parse_multipart(),因為它們在內部使用了受影響的函式。有關更多詳細資訊,請參閱它們各自的文件。(由 Adam Goldschmidt、Senthil Kumaran 和 Ken Jin 在 bpo-42967 中貢獻。)

Python 3.8.9 中的顯著變化

一項安全修復程式改變了 ftplib.FTP 的行為,使其在建立被動資料通道時不再信任遠端伺服器傳送的 IPv4 地址。我們改為重用 FTP 伺服器的 IP 地址。對於需要舊行為的特殊程式碼,請將 FTP 例項上的 trust_server_pasv_ipv4_address 屬性設定為 True。(參見 gh-87451)

Python 3.8.10 的顯著變化

macOS 11.0 (Big Sur) 和 Apple Silicon Mac 支援

自 3.8.10 起,Python 現在支援在 macOS 11 (Big Sur) 和 Apple Silicon Mac (基於 ARM64 架構) 上構建和執行。現在提供了一個新的通用構建變體 universal2,可以原生支援 ARM64Intel 64 的一套可執行檔案。請注意,Python 3.9 的此向後移植版本不包含對“弱連結”的支援,即構建針對較新 macOS 版本的二進位制檔案,這些檔案透過執行時測試缺失功能也能在舊版本上正確執行;為了支援一系列 macOS 版本,請繼續針對並構建在該範圍內的最舊版本上。

(最初由 Ronald Oussoren 和 Lawrence D’Anna 在 gh-85272 中貢獻,並由 FX Coudert 和 Eli Rykoff 修復,由 Maxime Bélanger 和 Ned Deily 向後移植到 3.8。)

Python 3.8.10 的顯著變化

urllib.parse

URL 部分中存在換行符或製表符可能導致某些形式的攻擊。根據更新 RFC 3986 的 WHATWG 規範,urllib.parse 中的解析器會從 URL 中去除 ASCII 換行符 \n\r 和製表符 \t,從而防止此類攻擊。刪除字元由新的模組級變數 urllib.parse._UNSAFE_URL_BYTES_TO_REMOVE 控制。(參見 bpo-43882)

Python 3.8.12 的顯著變化

Python API 的變化

從 Python 3.8.12 開始,ipaddress 模組不再接受 IPv4 地址字串中的任何前導零。前導零是模糊的,並且被某些庫解釋為八進位制表示法。例如,舊式函式 socket.inet_aton() 將前導零視為八進位制表示法。現代 inet_pton() 的 glibc 實現不接受任何前導零。

(最初由 Christian Heimes 在 bpo-36384 中貢獻,並由 Achraf Merzouki 向後移植到 3.8。)

3.8.14 中值得注意的安全特性

在基數不為 2(二進位制)、4、8(八進位制)、16(十六進位制)或 32(例如基數 10(十進位制))的情況下,在 intstr 之間進行轉換時,如果字串形式的位數超過限制,現在會引發 ValueError,以避免由於演算法複雜性而導致的潛在拒絕服務攻擊。這是針對 CVE 2020-10735 的緩解措施。此限制可以透過環境變數、命令列標誌或 sys API 進行配置或停用。請參見 整數字符串轉換長度限制 文件。預設限制為字串形式的 4300 位。

3.8.17 中的顯著變化

tarfile

  • tarfile 中的提取方法和 shutil.unpack_archive() 具有新的 filter 引數,允許限制可能令人驚訝或危險的 tar 功能,例如在目標目錄之外建立檔案。有關詳細資訊,請參見 提取過濾器。在 Python 3.12 中,不帶 filter 引數的使用將顯示 DeprecationWarning。在 Python 3.14 中,預設將切換為 'data'。(由 Petr Viktorin 在 PEP 706 中貢獻。)