Python 3.8 有什么新變化?

編者

Raymond Hettinger(譯者:wh2099 at outlook dot com)

本文解釋了 Python 3.8 相比 3.7 的新增特性。 完整的詳情可參閱 更新日志。

摘要 -- 發(fā)布重點(diǎn)?

新的特性?

賦值表達式?

新增的語(yǔ)法 := 可在表達式內部為變量賦值。 它被昵稱(chēng)為“海象運算符”因為它很像是 海象的眼睛和長(cháng)牙。

在這個(gè)示例中,賦值表達式可以避免調用 len() 兩次:

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

類(lèi)似的益處還可出現在正則表達式匹配中需要使用兩次匹配對象的情況中,一次檢測用于匹配是否發(fā)生,另一次用于提取子分組:

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

此運算符也適用于配合 while 循環(huán)計算一個(gè)值來(lái)檢測循環(huán)是否終止,而同一個(gè)值又在循環(huán)體中再次被使用的情況:

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

另一個(gè)值得介紹的用例出現于列表推導式中,在篩選條件中計算一個(gè)值,而同一個(gè)值又在表達式中需要被使用:

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

請盡量將海象運算符的使用限制在清晰的場(chǎng)合中,以降低復雜性并提升可讀性。

請參閱 PEP 572 了解詳情。

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

僅限位置形參?

新增了一個(gè)函數形參語(yǔ)法 / 用來(lái)指明某些函數形參必須使用僅限位置而非關(guān)鍵字參數的形式。 這種標記語(yǔ)法與通過(guò) help() 所顯示的使用 Larry Hastings 的 Argument Clinic 工具標記的 C 函數相同。

在下面的例子中,形參 ab 為僅限位置形參,cd 可以是位置形參或關(guān)鍵字形參,而 ef 要求為關(guān)鍵字形參:

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

這種標記形式的一個(gè)用例是它允許純 Python 函數完整模擬現有的用 C 代碼編寫(xiě)的函數的行為。 例如,內置的 divmod() 函數不接受關(guān)鍵字參數:

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

另一個(gè)用例是在不需要形參名稱(chēng)時(shí)排除關(guān)鍵字參數。 例如,內置的 len() 函數的簽名為 len(obj, /)。 這可以排除如下這種笨拙的調用形式:

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

另一個(gè)益處是將形參標記為僅限位置形參將允許在未來(lái)修改形參名而不會(huì )破壞客戶(hù)的代碼。 例如,在 statistics 模塊中,形參名 dist 在未來(lái)可能被修改。 這使得以下函數描述成為可能:

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

由于在 / 左側的形參不會(huì )被公開(kāi)為可用關(guān)鍵字,其他形參名仍可在 **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}

這極大地簡(jiǎn)化了需要接受任意關(guān)鍵字參數的函數和方法的實(shí)現。 例如,以下是一段摘自 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) 可將隱式的字節碼緩存配置為使用單獨的并行文件系統樹(shù),而不是默認的每個(gè)源代碼目錄下的 __pycache__ 子目錄。

緩存的位置會(huì )在 sys.pycache_prefix 中報告 (None 表示默認位置即 __pycache__ 子目錄)。

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

調試構建使用與發(fā)布構建相同的 ABI?

Python 現在不論是以發(fā)布模式還是調試模式進(jìn)行構建都將使用相同的 ABI。 在 Unix 上,當 Python 以調試模式構建時(shí),現在將可以加載以發(fā)布模式構建的 C 擴展和使用穩定版 ABI 構建的 C 擴展。

發(fā)布編譯版和 調試編譯版 現在都是 ABI 兼容的:定義 Py_DEBUG 宏不會(huì )再應用 Py_TRACE_REFS 宏,它引入 了唯一的 ABI 不兼容性。 Py_TRACE_REFS 宏添加了 sys.getobjects() 函數和 PYTHONDUMPREFS 環(huán)境變量,它可以使用新的 ./configure --with-trace-refs 編譯選項來(lái)設置。 (由 Victor Stinner 在 bpo-36465 中貢獻。)

在 Unix 上,C 擴展不會(huì )再被鏈接到 libpython,但 Android 和 Cygwin 例外。 現在靜態(tài)鏈接的 Python 將可以加載使用共享庫 Python 構建的 C 擴展。 (由 Victor Stinner 在 bpo-21536 中貢獻。)

在 Unix 上,當 Python 以調試模式構建時(shí),導入操作現在也會(huì )查找在發(fā)布模式下編譯的 C 擴展以及使用穩定版 ABI 編譯的 C 擴展。 (由 Victor Stinner 在 bpo-36722 中貢獻。)

要將 Python 嵌入到一個(gè)應用中,必須將新增的 --embed 選項傳給 python3-config --libs --embed 以獲得 -lpython3.8 (將應用鏈接到 libpython)。 要同時(shí)支持 3.8 和舊版本,請先嘗試 python3-config --libs --embed 并在此命令失敗時(shí)回退到 python3-config --libs (即不帶 --embed)。

增加一個(gè) pkg-config python-3.8-embed 模塊用來(lái)將 Python 嵌入到一個(gè)應用中: pkg-config python-3.8-embed --libs 包含 -lpython3.8。 要同時(shí)支持 3.8 和舊版本,請先嘗試 pkg-config python-X.Y-embed --libs 并在此命令失敗時(shí)回退到 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-字符串支持 = 用于自動(dòng)記錄表達式和調試文檔?

增加 = 說(shuō)明符用于 f-string。 形式為 f'{expr=}' 的 f-字符串將擴展表示為表達式文本,加一個(gè)等于號,再加表達式的求值結果。 例如:

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

通常的 f-字符串格式說(shuō)明符 允許更細致地控制所要顯示的表達式結果:

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

= 說(shuō)明符將輸出整個(gè)表達式,以便詳細演示計算過(guò)程:

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

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

PEP 578: Python 運行時(shí)審核鉤子?

此 PEP 添加了審核鉤子和已驗證開(kāi)放鉤子。 兩者在 Python 與本機代碼中均可用。允許以純 Python 代碼編寫(xiě)的應用和框架利用額外的通知,同時(shí)允許嵌入開(kāi)發(fā)人員或系統管理員部署始終啟用審核的 Python 版本。

請參閱 PEP 578 了解詳情。

PEP 587: Python 初始化配置?

PEP 587 增加了一個(gè)新的 C API 用來(lái)配置 Python 初始化,提供對整個(gè)配置過(guò)程的更細致控制以及更好的錯誤報告。

新的結構:

新的函數:

此 PEP 還為這些內部結構添加了 _PyRuntimeState.preconfig (PyPreConfig 類(lèi)型) 和 PyInterpreterState.config (PyConfig 類(lèi)型) 字段。 PyInterpreterState.config 成為新的引用配置,替代全局配置變量和其他私有變量。

請參閱 Python 初始化配置 獲取詳細文檔。

請參閱 PEP 587 了解詳情。

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

PEP 590: Vectorcall: 用于 CPython 的快速調用協(xié)議?

Vectorcall 協(xié)議 添加到 Python/C API。 它的目標是對已被應用于多個(gè)類(lèi)的現有優(yōu)先進(jìn)行正式化。 任何實(shí)現了可調用對象的 靜態(tài)類(lèi)型 均可使用此協(xié)議。

此特性目前為暫定狀態(tài),計劃在 Python 3.9 將其完全公開(kāi)。

請參閱 PEP 590 了解詳情。

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

具有外部數據緩沖區的 pickle 協(xié)議 5?

當使用 pickle 在 Python 進(jìn)程間傳輸大量數據以充分發(fā)揮多核或多機處理的優(yōu)勢時(shí),非常重要一點(diǎn)是通過(guò)減少內存拷貝來(lái)優(yōu)化傳輸效率,并可能應用一些定制技巧例如針對特定數據的壓縮。

pickle 協(xié)議 5 引入了對于外部緩沖區的支持,這樣 PEP 3118 兼容的數據可以與主 pickle 流分開(kāi)進(jìn)行傳輸,這是由通信層來(lái)確定的。

請參閱 PEP 574 了解詳情。

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

其他語(yǔ)言特性修改?

  • 在之前版本中 continue 語(yǔ)句不允許在 finally 子句中使用,這是因為具體實(shí)現存在一個(gè)問(wèn)題。 在 Python 3.8 中此限制已被取消。 (由 Serhiy Storchaka 在 bpo-32489 中貢獻。)

  • bool, intfractions.Fraction 類(lèi)型現在都有一個(gè) as_integer_ratio() 方法,與 floatdecimal.Decimal 中的已有方法類(lèi)似。 這個(gè)微小的 API 擴展使得 numerator, denominator = x.as_integer_ratio() 這樣的寫(xiě)法在多種數字類(lèi)型上通用成為可能。 (由 Lisa Roach 在 bpo-33073 和 Raymond Hettinger 在 bpo-37819 中貢獻。)

  • int, floatcomplex 的構造器現在會(huì )使用 __index__() 特殊方法,如果該方法可用而對應的方法 method __int__(), __float__()__complex__() 方法不可用的話(huà)。 (由 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 和 dictview 可以使用 reversed() 按插入順序反向迭代。 (由 Rémi Lapeyre 在 bpo-33462 中貢獻。)

  • 在函數調用中允許使用的關(guān)鍵字名稱(chēng)語(yǔ)法受到進(jìn)一步的限制。 特別地,f((keyword)=arg) 不再被允許。 關(guān)鍵字參數賦值形式的左側絕不允許一般標識符以外的其他內容。 (由 Benjamin Peterson 在 bpo-34641 中貢獻。)

  • yieldreturn 語(yǔ)句中的一般可迭代對象解包不再要求加圓括號。 這使得 yieldreturn 的語(yǔ)法與正常的賦值語(yǔ)法更為一致:

    >>>
    >>> 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 中貢獻。)

  • 當類(lèi)似 [(10, 20) (30, 40)] 這樣在代碼中少了一個(gè)逗號時(shí),編譯器將顯示 SyntaxWarning 并附帶更有幫助的提示。 這相比原來(lái)用 TypeError 來(lái)提示第一個(gè)元組是不可調用的更容易被理解。 (由 Serhiy Storchaka 在 bpo-15248 中貢獻。)

  • datetime.datedatetime.datetimedatetime.timedelta 對象之間的算術(shù)運算現在將返回相應子類(lèi)的實(shí)例而不是基類(lèi)的實(shí)例。 這也會(huì )影響到在具體實(shí)現中(直接或間接地)使用了 datetime.timedelta 算術(shù)運算的返回類(lèi)型,例如 astimezone()。 (由 Paul Ganssle 在 bpo-32417 中貢獻。)

  • 當 Python 解釋器通過(guò) Ctrl-C (SIGINT) 被中斷并且所產(chǎn)生的 KeyboardInterrupt 異常未被捕獲,Python 進(jìn)程現在會(huì )通過(guò)一個(gè) SIGINT 信號或是使得發(fā)起調用的進(jìn)程能檢測到它是由 Ctrl-C 操作殺死的正確退出代碼來(lái)退出。 POSIX 和 Windows 上的終端會(huì )相應地使用此代碼在交互式會(huì )話(huà)中終止腳本。 (由 Google 的 Gregory P. Smith 在 bpo-1054041 中貢獻。)

  • 某些高級編程風(fēng)格要求為現有的函數更新 types.CodeType 對象。 由于代碼對象是不可變的,需要基于現有代碼對象模型創(chuàng )建一個(gè)新的代碼對象。 使用 19 個(gè)形參將會(huì )相當繁瑣。 現在,新的 replace() 方法使得通過(guò)少量修改的形參創(chuàng )建克隆對象成為可能。

    下面是一個(gè)修改 statistics.mean() 函數來(lái)防止 data 形參被用作關(guān)鍵字參數的例子:

    >>>
    >>> 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() 函數的三參數形式在底數與模數不可約的情況下允許指數為負值。 隨后它會(huì )在指數為 -1 時(shí)計算底數的模乘逆元,并對其他負指數計算反模的適當冪次。 例如,要計算 38 模 137 的 模乘逆元 則可寫(xiě)為:

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

    模乘逆元在求解 線(xiàn)性丟番圖方程 會(huì )被用到。 例如,想要求出 4258?? + 147?? = 369 的整數解,首先應重寫(xiě)為 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 中貢獻。)

  • 字典推導式已與字典字面值實(shí)現同步,會(huì )先計算鍵再計算值:

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

    對執行順序的保證對賦值表達式來(lái)說(shuō)很有用,因為在鍵表達式中賦值的變量將可在值表達式中被使用:

    >>>
    >>> 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__() 方法現在可返回長(cháng)度為二至六個(gè)元素的元組。 之前的上限為五個(gè)。 新增的第六個(gè)可選元素是簽名為 (obj, state) 的可調用對象。 這樣就允許直接控制特定對象的狀態(tài)更新。 如果元素值不為 None,該可調用對象將優(yōu)先于對象的 __setstate__() 方法。 (由 Pierre Glaser 和 Olivier Grisel 在 bpo-35900 中貢獻。)

新增模塊?

  • 新增的 importlib.metadata 模塊提供了從第三方包讀取元數據的(臨時(shí))支持。 例如,它可以提取一個(gè)已安裝軟件包的版本號、入口點(diǎn)列表等等:

    >>>
    >>> # 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 中貢獻)。

改進(jìn)的模塊?

ast?

AST 節點(diǎn)現在具有 end_linenoend_col_offset 屬性,它們給出節點(diǎn)結束的精確位置。 (這只適用于具有 linenocol_offset 屬性的節點(diǎn)。)

新增函數 ast.get_source_segment() 返回指定 AST 節點(diǎn)的源代碼。

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

ast.parse() 函數具有一些新的旗標:

  • type_comments=True 導致其返回與特定 AST 節點(diǎn)相關(guān)聯(lián)的 PEP 484PEP 526 類(lèi)型注釋文本;

  • mode='func_type' 可被用于解析 PEP 484 "簽名類(lèi)型注釋" (為函數定義 AST 節點(diǎn)而返回);

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

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

asyncio?

asyncio.run() 已經(jīng)從暫定狀態(tài)晉級為穩定 API。 此函數可被用于執行一個(gè) coroutine 并返回結果,同時(shí)自動(dòng)管理事件循環(huán)。 例如:

import asyncio

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

asyncio.run(main())

大致 等價(jià)于:

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

實(shí)際的實(shí)現要更復雜許多。 因此 asyncio.run() 應該作為運行 asyncio 程序的首選方式。

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

運行 python -m asyncio 將啟動(dòng)一個(gè)原生異步 REPL。 這允許快速體驗具有最高層級 await 的代碼。 這時(shí)不再需要直接調用 asyncio.run(),因為此操作會(huì )在每次發(fā)起調用時(shí)產(chǎn)生一個(gè)新事件循環(huán):

$ 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 上,現在默認的事件循環(huán)為 ProactorEventLoop。 (由 Victor Stinner 在 bpo-34687 中貢獻。)

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

ProactorEventLoop 現在可通過(guò) KeyboardInterrupt ("CTRL+C") 來(lái)中斷。 (由 Vladimir Matveev 在 bpo-23057 中貢獻。)

添加了 asyncio.Task.get_coro() 用來(lái)獲取 asyncio.Task 中的已包裝協(xié)程。 (由 Alex Gr?nholm 在 bpo-36999 中貢獻。)

asyncio 任務(wù)現在可以被命名,或者是通過(guò)將 name 關(guān)鍵字參數傳給 asyncio.create_task()create_task() 事件循環(huán)方法,或者是通過(guò)在任務(wù)對象上調用 set_name() 方法。 任務(wù)名稱(chēng)在 asyncio.Taskrepr() 輸出中可見(jiàn),并且還可以使用 get_name() 方法來(lái)獲取。 (由 Alex Gr?nholm 在 bpo-34270 中貢獻。)

將對 Happy Eyeballs 的支持添加到 asyncio.loop.create_connection()。 要指定此行為,已增加了兩個(gè)新的形參: happy_eyeballs_delayinterleave。 Happy Eyeballs 算法可提升支持 IPv4 和 IPv6 的應用的響應速度,具體做法是嘗試同時(shí)使用兩者進(jìn)行連接。 (由 twisteroid ambassador 在 bpo-33530 中貢獻。)

builtins?

內置的 compile() 已改進(jìn)為可接受 ast.PyCF_ALLOW_TOP_LEVEL_AWAIT 旗標。 當傳入此新旗標時(shí),compile() 將允許通常被視為無(wú)效語(yǔ)法的最高層級 await, async forasync with 構造。 此后將可返回帶有 CO_COROUTINE 旗標的異步代碼對象。 (由 Matthias Bussonnier 在 bpo-34616 中貢獻。)

collections?

collections.namedtuple()_asdict() 方法現在將返回 dict 而不是 collections.OrderedDict。 此項更改是由于普通字典自 Python 3.7 起已保證具有確定的元素順序。 如果還需要 OrderedDict 的額外特性,建議的解決方案是將結果轉換為需要的類(lèi)型: OrderedDict(nt._asdict())。 (由 Raymond Hettinger 在 bpo-35864 中貢獻。)

cProfile?

cProfile.Profile 類(lèi)現在可被用作上下文管理器。 在運行時(shí)對一個(gè)代碼塊實(shí)現性能分析:

import cProfile

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

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

csv?

csv.DictReader 現在將返回 dict 而不是 collections.OrderedDict。 此工具現在會(huì )更快速且消耗更少內存同時(shí)仍然保留字段順序。 (由 Michael Selik 在 bpo-34003 中貢獻。)

curses?

添加了一個(gè)新變量用于保存下層 ncurses 庫的結構版信息: ncurses_version。 (由 Serhiy Storchaka 在 bpo-31680 中貢獻。)

ctypes?

在 Windows 上,CDLL 及其子類(lèi)現在接受 winmode 形參來(lái)指定用于底層 LoadLibraryEx 調用的旗標。 默認旗標被設為僅加載來(lái)自可信任位置的 DLL 依賴(lài)項,包括 DLL 的存放路徑(如果加載初始 DLL 時(shí)使用了完整或部分路徑)以及通過(guò) add_dll_directory() 添加的路徑。 (由 Steve Dower 在 bpo-36085 中貢獻。)

datetime?

添加了新的替代構造器 datetime.date.fromisocalendar()datetime.datetime.fromisocalendar(),它們分別基于 ISO 年份、周序號和周內日序號來(lái)構造 datedatetime 對象;這兩者分別是其所對應類(lèi)中 isocalendar 方法的逆操作。 (由 Paul Ganssle 在 bpo-36004 中貢獻。)

functools?

functools.lru_cache() 現在可直接作為裝飾器而不是作為返回裝飾器的函數。 因此這兩種寫(xiě)法現在都被支持:

@lru_cache
def f(x):
    ...

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

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

添加了新的 functools.cached_property() 裝飾器,用于在實(shí)例生命周期內緩存的已計算特征屬性。

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() 裝飾器可使用 single dispatch 將方法轉換為 泛型函數:

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() 現在能接受一個(gè)可選的 generation 形參來(lái)指定一個(gè)用于獲取對象的生成器。 (由 Pablo Galindo 在 bpo-36016 中貢獻。)

gettext?

添加了 pgettext() 及其變化形式。 (由 Franz Glasner, éric Araujo 和 Cheryl Sabella 在 bpo-2504 中貢獻。)

gzip?

添加 mtime 形參到 gzip.compress() 用于可重現的輸出。 (由 Guo Ci Teo 在 bpo-34898 中貢獻。)

對于特定類(lèi)型的無(wú)效或已損壞 gzip 文件現在將引發(fā) BadGzipFile 而不是 OSError。 (由 Filip Gruszczyński, Michele Orrù 和 Zackery Spytz 在 bpo-6584 中貢獻。)

IDLE 與 idlelib?

超過(guò) N 行(默認值為 50)的輸出將被折疊為一個(gè)按鈕。 N 可以在 Settings 對話(huà)框的 General 頁(yè)的 PyShell 部分中進(jìn)行修改。 數量較少但是超長(cháng)的行可以通過(guò)在輸出上右擊來(lái)折疊。 被折疊的輸出可通過(guò)雙擊按鈕來(lái)展開(kāi),或是通過(guò)右擊按鈕來(lái)放入剪貼板或是單獨的窗口。 (由 Tal Einat 在 bpo-1529353 中貢獻。)

在 Run 菜單中增加了 "Run Customized" 以使用自定義設置來(lái)運行模塊。 輸入的任何命令行參數都會(huì )被加入 sys.argv。 它們在下次自定義運行時(shí)會(huì )再次顯示在窗體中。 用戶(hù)也可以禁用通常的 Shell 主模塊重啟。 (由 Cheryl Sabella, Terry Jan Reedy 等人在 bpo-5680bpo-37627 中貢獻。)

在 IDLE 編輯器窗口中增加了可選的行號。 窗口打開(kāi)時(shí)默認不顯示行號,除非在配置對話(huà)框的 General 選項卡中特別設置。 已打開(kāi)窗口中的行號可以在 Options 菜單中顯示和隱藏。 (由 Tal Einat 和 Saimadhav Heblikar 在 bpo-17535 中貢獻。)

現在會(huì )使用 OS 本機編碼格式在 Python 字符串和 Tcl 對象間進(jìn)行轉換。 這允許在 IDLE 中處理 emoji 和其他非 BMP 字符。 這些字符將可被顯示或是從剪貼板復制和粘貼。 字符串從 Tcl 到 Python 的來(lái)回轉換現在不會(huì )再發(fā)生失敗。 (過(guò)去八年有許多人都為此付出過(guò)努力,問(wèn)題最終由 Serhiy Storchaka 在 bpo-13153 中解決。)

在 3.8.1 中新增:

添加切換光標閃爍停止的選項。 (由 Zackery Spytz 在 bpo-4603 中貢獻。)

Esc 鍵現在會(huì )關(guān)閉 IDLE 補全提示窗口。 (由 Johnny Najera 在 bpo-38944 中貢獻。)

上述修改已被反向移植到 3.7 維護發(fā)行版中。

添加關(guān)鍵字到模塊名稱(chēng)補全列表。 (由 Terry J. Reedy 在 bpo-37765 中貢獻。)

inspect?

inspect.getdoc() 函數現在可以找到 __slots__ 的文檔字符串,如果該屬性是一個(gè)元素值為文檔字符串的 dict 的話(huà)。 這提供了類(lèi)似于目前已有的 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?

在開(kāi)發(fā)模式 (-X env) 和 調試編譯版 中,io.IOBase 終結器現在會(huì )在 close() 方法失敗時(shí)將異常寫(xiě)入日志。 發(fā)生的異常在發(fā)布編譯版中會(huì )被靜默地忽略。 (由 Victor Stinner 在 bpo-18748 中貢獻。)

itertools?

itertools.accumulate() 函數增加了可選的 initial 關(guān)鍵字參數用來(lái)指定一個(gè)初始值:

>>>
>>> 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 用于將每個(gè)輸入行解析為單獨的 JSON 對象。 (由 Weipeng Hong 在 bpo-31553 中貢獻。)

logging?

logging.basicConfig() 添加了 force 關(guān)鍵字參數,當設為真值時(shí),關(guān)聯(lián)到根日志記錄器的任何現有處理程序都將在執行由其他參數所指定的配置之前被移除并關(guān)閉。

這解決了一個(gè)長(cháng)期存在的問(wèn)題。 當一個(gè)日志處理器或 basicConfig() 被調用時(shí),對 basicConfig() 的后續調用會(huì )被靜默地忽略。 這導致使用交互提示符或 Jupyter 筆記本更新、試驗或講解各種日志配置選項變得相當困難。

(由 Raymond Hettinger 提議,由 Dong-hee Na 實(shí)現,并由 Vinay Sajip 在 bpo-33897 中完成審核。)

math?

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

擴展了 math.hypot() 函數以便處理更多的維度。 之前它僅支持 2-D 的情況。 (由 Raymond Hettinger 在 bpo-33089 中貢獻。)

添加了新的函數 math.prod() 作為的 sum() 同類(lèi),該函數返回 'start' 值 (默認值: 1) 乘以一個(gè)數字可迭代對象的積:

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

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

添加了兩個(gè)新的組合函數 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-37128, bpo-37178bpo-35431 中貢獻。)

添加了一個(gè)新函數 math.isqrt() 用于計算精確整數平方根而無(wú)需轉換為浮點(diǎn)數。 該新函數支持任意大整數。 它的執行速度比 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() 不再接受非整數類(lèi)參數。 (由 Pablo Galindo 在 bpo-33083 中貢獻。)

mmap?

mmap.mmap 類(lèi)現在具有一個(gè) madvise() 方法用于訪(fǎng)問(wèn) madvise() 系統調用。 (由 Zackery Spytz 在 bpo-32941 中貢獻。)

multiprocessing?

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

在macOS上,現在默認使用的啟動(dòng)方式是*spawn*啟動(dòng)方式。 (由 Victor Stinner 在 bpo-33725 中貢獻。)

os?

在 Windows 上添加了新函數 add_dll_directory() 用于在導入擴展模塊或使用 ctypes 加載 DLL 時(shí)為本機依賴(lài)提供額外搜索路徑 。 (由 Steve Dower 在 bpo-36085 中貢獻。)

添加了新的 os.memfd_create() 函數用于包裝 memfd_create() 系統調用。 (由 Zackery Spytz 和 Christian Heimes 在 bpo-26836 中貢獻。)

在 Windows 上,大部分用于處理重解析點(diǎn),(包括符號鏈接和目錄連接)的手動(dòng)邏輯已被委托給操作系統。 特別地,os.stat() 現在將會(huì )遍歷操作系統所支持的任何內容,而 os.lstat() 將只打開(kāi)被標識為“名稱(chēng)代理”的重解析點(diǎn),而其要由 os.stat() 打開(kāi)其他的重解析點(diǎn)。 在所有情況下,stat_result.st_mode 將只為符號鏈接而非其他種類(lèi)的重解析點(diǎn)設置 S_IFLNK。 要標識其他種類(lèi)的重解析點(diǎn),請檢查新的 stat_result.st_reparse_tag 屬性。

在 Windows 上,os.readlink() 現在能夠讀取目錄連接。 請注意 islink() 會(huì )對目錄連接返回 False,因此首先檢查 islink 的代碼將連續把連接視為目錄,而會(huì )處理 os.readlink() 所引發(fā)錯誤的代碼現在會(huì )把連接視為鏈接。

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

os.path?

返回布爾值結果的 os.path 函數例如 exists(), lexists(), isdir(), isfile(), islink(), 以及 ismount() 現在對于包含在 OS 層級無(wú)法表示的字符或字節的路徑將會(huì )返回 False 而不是引發(fā) ValueError 或其子類(lèi) UnicodeEncodeErrorUnicodeDecodeError。 (由 Serhiy Storchaka 在 bpo-33721 中貢獻。)

expanduser() 在 Windows 上現在改用 USERPROFILE 環(huán)境變量而不再使用 HOME,后者通常不會(huì )為一般用戶(hù)賬戶(hù)設置。 (由 Anthony Sottile 在 bpo-36264 中貢獻。)

isdir() 在 Windows 上將不再為不存在的目錄的鏈接返回 True。

realpath() 在 Windows 上現在會(huì )識別重解析點(diǎn),包括符號鏈接和目錄連接。

(由 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() 現在對于包含在 OS 層級無(wú)法表示的字符或字節的路徑將會(huì )返回 False 而不是引發(fā) ValueError 或其子類(lèi) UnicodeEncodeError。 (由 Serhiy Storchaka 在 bpo-33721 中貢獻。)

Added pathlib.Path.link_to() which creates a hard link pointing to a path. (Contributed by Joannah Nanjekye in bpo-26978) Note that link_to was deprecated in 3.10 and removed in 3.12 in favor of a hardlink_to method added in 3.10 which matches the semantics of the existing symlink_to method.

pickle?

pickle 擴展子類(lèi)化針對 C 優(yōu)化的 Pickler 現在可通過(guò)定義特殊的 reducer_override() 方法來(lái)重載函數和類(lèi)的封存邏輯。 (由 Pierre Glaser 和 Olivier Grisel 在 bpo-35900 中貢獻。)

plistlib?

添加了新的 plistlib.UID 并啟動(dòng)了對讀取和寫(xiě)入經(jīng)過(guò) NSKeyedArchiver 編碼的二進(jìn)制 plists 的支持。 (由 Jon Janzen 在 bpo-26707 中貢獻。)

pprint?

pprint 模塊為一些函數添加了 sort_dicts 形參。 默認情況下,這些函數會(huì )繼續在渲染或打印之前對字典進(jìn)行排序。 但是,如果 sort_dicts 設為假值,則字典將保持鍵插入時(shí)的順序。 這在調試期間與 JSON 輸入進(jìn)行比較時(shí)會(huì )很有用。

除此之外,還增加了一個(gè)方便的新函數 pprint.pp(),它類(lèi)似于 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 關(guān)鍵字參數。 (由 Josh Bronson 在 bpo-20849 中貢獻。)

shutil.make_archive() 現在對新的歸檔默認使用 modern pax (POSIX.1-2001) 格式以提升可移植性和標準一致性,此特性繼承自對 tarfile 模塊的相應更改。 (由 C.A.M. Gerlach 在 bpo-30661 中貢獻。)

shutil.rmtree() 在 Windows 上現在會(huì )移除目錄連接而不會(huì )遞歸地先移除其中的內容。 (由 Steve Dower 在 bpo-37834 中貢獻。)

socket?

添加了便捷的 create_server()has_dualstack_ipv6() 函數以自動(dòng)化在創(chuàng )建服務(wù)器套接字時(shí)通常情況下所必須的任務(wù),包括在同一套接字中同時(shí)接受 IPv4 和 IPv6 連接。 (由 Giampaolo Rodolà 在 bpo-17561 中貢獻。)

socket.if_nameindex(), socket.if_nametoindex()socket.if_indextoname() 函數已經(jīng)在 Windows 上實(shí)現。 (由 Zackery Spytz 在 bpo-37007 中貢獻。)

ssl?

增加了 post_handshake_authverify_client_post_handshake() 分別啟用和初始化 TLS 1.3 握手后驗證。 (由 Christian Heimes 在 bpo-34670 中貢獻。)

statistics?

添加了 statistics.fmean() 作為 statistics.mean() 的更快速的浮點(diǎn)數版版本。 (由 Raymond Hettinger 和 Steven D'Aprano 在 bpo-35904 中貢獻。)

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

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

添加了 statistics.quantiles() 用于將數據或分布劃分為多個(gè)等概率區間(例如四分位、十分位或百分位)。 (由 Raymond Hettinger 在 bpo-36546 中貢獻。)

添加了 statistics.NormalDist 用于創(chuàng )建和操縱隨機變量的正態(tài)分布。 (由 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() 函數,可被重載以便控制如何處理“不可引發(fā)的異?!?。 它會(huì )在發(fā)生了一個(gè)異常但 Python 沒(méi)有辦法處理時(shí)被調用。 例如,當一個(gè)析構器在垃圾回收時(shí) (gc.collect()) 所引發(fā)的異常。 (由 Victor Stinner 在 bpo-36829 中貢獻。)

tarfile?

tarfile 模塊現在對新的歸檔默認使用 modern pax (POSIX.1-2001) 格式而不再是之前的 GNU 專(zhuān)屬格式。 這通過(guò)標準化和可擴展格式的統一編碼 (UTF-8) 提升了跨平臺可移植性,還提供了其他一些益處。 (由 C.A.M. Gerlach 在 bpo-36268 中貢獻。)

threading?

添加了新的 threading.excepthook() 函數用來(lái)處理未捕獲的 threading.Thread.run() 異常。 它可被重載以便控制如何處理未捕獲的 threading.Thread.run() 異常。 (由 Victor Stinner 在 bpo-1230540 中貢獻。)

添加了新的 threading.get_native_id() 函數以及 threading.Thread 類(lèi)的 native_id 屬性。 它們會(huì )返回內核所分配給當前線(xiàn)程的原生整數線(xiàn)程 ID。 此特性?xún)H在特定平臺上可用,參見(jiàn) get_native_id 了解詳情。 (由 Jake Tesler 在 bpo-36084 中貢獻。)

tokenize?

當提供不帶末尾新行的輸入時(shí),tokenize 模塊現在會(huì )隱式地添加 NEWLINE 形符。 此行為現在已與 C 詞法分析器的內部行為相匹配。 (由 Ammar Askar 在 bpo-33899 中貢獻。)

tkinter?

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

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

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

time?

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

typing?

typing 模塊加入了一些新特性:

  • 一個(gè)帶有鍵專(zhuān)屬類(lèi)型的字典類(lèi)型。 參見(jiàn) PEP 589typing.TypedDict。 TypedDict 只使用字符串作為鍵。 默認情況下每個(gè)鍵都要求提供。 指定 "total=False" 以允許鍵作為可選項:

    class Location(TypedDict, total=False):
        lat_long: tuple
        grid_square: str
        xy_coordinate: tuple
    
  • Literal 類(lèi)型。 參見(jiàn) PEP 586typing.Literal。 Literal 類(lèi)型指明一個(gè)形參或返回值被限定為一個(gè)或多個(gè)特定的字面值:

    def get_status(port: int) -> Literal['connected', 'disconnected']:
        ...
    
  • "Final" 變量、函數、方法和類(lèi)。 參見(jiàn) PEP 591, typing.Finaltyping.final()。 final 限定符會(huì )指示靜態(tài)類(lèi)型檢查器限制進(jìn)行子類(lèi)化、重載或重新賦值:

    pi: Final[float] = 3.1415926536
    
  • 協(xié)議定義。 參見(jiàn) PEP 544, typing.Protocoltyping.runtime_checkable()。 簡(jiǎn)單的 ABC 例如 typing.SupportsInt 現在是 Protocol 的子類(lèi)。

  • 新的協(xié)議類(lèi) typing.SupportsIndex。

  • 新的函數 typing.get_origin()typing.get_args()。

unicodedata?

unicodedata 模塊現在已升級為使用 Unicode 12.1.0 發(fā)布版。

新的函數 is_normalized() 可被用來(lái)驗證字符串是否為特定正規形式,通常會(huì )比實(shí)際進(jìn)行字符串正規化要快得多。 (由 Max Belanger, David Euresti 和 Greg Price 在 bpo-32285bpo-37966 中貢獻。)

unittest?

添加了 AsyncMock 以支持異步版本的 Mock。 同時(shí)也添加了相應的斷言函數用于測試。 (由 Lisa Roach 在 bpo-26467 中貢獻。)

unittest 添加了 addModuleCleanup()addClassCleanup() 以支持對 setUpModule()setUpClass() 進(jìn)行清理。 (由 Lisa Roach 在 bpo-24412 中貢獻。)

一些模擬斷言函數現在也會(huì )在失敗時(shí)打印一個(gè)實(shí)際調用列表。 (由 Petter Strandmark 在 bpo-35047 中貢獻。)

unittest 模塊已支持通過(guò) unittest.IsolatedAsyncioTestCase 來(lái)使用協(xié)程作為測試用例。 (由 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 在所有平臺上都會(huì )包含 Activate.ps1 腳本用于在 PowerShell Core 6.1 下激活虛擬環(huán)境。 (由 Brett Cannon 在 bpo-32718 中貢獻。)

weakref?

weakref.proxy() 返回的代理對象現在除其他算術(shù)運算符外也支持矩陣乘法運算符 @@=。 (由 Mark Dickinson 在 bpo-36669 中貢獻。)

xml?

作為對 DTD 和外部實(shí)體檢索的緩解,在默認情況下 xml.dom.minidomxml.sax 模塊不再處理外部實(shí)體。 (由 Christian Heimes 在 bpo-17239 中貢獻。)

xml.etree.ElementTree 模塊中的 .find*() 方法支持通配符搜索例如 {*}tag,此搜索會(huì )忽略命名空間以及返回給定命名空間中所有標簽的 {namespace}*。 (由 Stefan Behnel 在 bpo-28238 中貢獻。)

xml.etree.ElementTree 模塊提供了實(shí)現 C14N 2.0 的新函數 –xml.etree.ElementTree.canonicalize()。 (由 Stefan Behnel 在 bpo-13611 中貢獻。)

xml.etree.ElementTree.XMLParser 的目標對象可通過(guò)新的回調方法 start_ns()end_ns() 來(lái)接受命名空間聲明事件。 此外,xml.etree.ElementTree.TreeBuilder 目標可被配置為處理有關(guān)注釋和處理指令事件以將它們包含在所生成的樹(shù)當中。 (由 Stefan Behnel 在 bpo-36676bpo-36673 中貢獻。)

xmlrpc?

xmlrpc.client.ServerProxy 現在支持可選的 headers 關(guān)鍵字參數作為隨同每次請求發(fā)送的 HTTP 標頭序列。 此特征的作用之一是使得從默認的基礎認證升級到更快速的會(huì )話(huà)認證成為可能。 (由 Cédric Krier 在 bpo-35153 中貢獻。)

性能優(yōu)化?

  • subprocess 模塊現在能在某些情況下使用 os.posix_spawn() 函數以獲得更好的性能。 目前,它的使用僅限 macOS 和 Linux(使用 glibc 2.24 或更新版本),并要求滿(mǎn)足以下條件:

    • close_fds 為假值;

    • preexec_fn, pass_fds, cwdstart_new_session 形參未設置;

    • executable 路徑包含一個(gè)目錄。

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

  • shutil.copyfile(), shutil.copy(), shutil.copy2(), shutil.copytree()shutil.move() 在 Linux 和 macOS 上會(huì )使用平臺專(zhuān)屬的 "fast-copy" 系統調用以提高效率。 "fast-copy" 意味著(zhù)拷貝操作發(fā)生于內核中,從而避免在進(jìn)行 "outfd.write(infd.read())" 等操作時(shí)使用 Python 中的用戶(hù)空間緩沖區。 在 Windows 上 shutil.copyfile() 會(huì )使用更大的默認緩沖區(1 MiB 而不是 16 KiB)并且使用基于 memoryview()shutil.copyfileobj() 版本。 在同一分區內拷貝一個(gè) 512 MiB 文件的速度提升在 Linux 上約為 +26%,在 macOS 上為 +50%,在 Windows 上為 +40%。 此外還將消耗更少的 CPU 周期。 參見(jiàn) 依賴(lài)于具體平臺的高效拷貝操作 一節。 (由 Giampaolo Rodolà 在 bpo-33671 中貢獻。)

  • shutil.copytree() 會(huì )根據其所用緩存的 os.stat() 值使用 os.scandir() 函數及所有拷貝函數。 拷貝一個(gè)包含 8000 文件的目錄的速度提升在 Linux 上約為 +9%,在 Windows 上為 +20%,對于 Windows SMB 共享目錄則為 +30%。 此外 os.stat() 系統調用的次數也減少了 38%,使得 shutil.copytree() 在網(wǎng)絡(luò )文件系統上會(huì )特別快速。 (由Giampaolo Rodolà 在 bpo-33695 中貢獻。)

  • pickle 模塊使用的默認協(xié)議現在為 Protocol 4,最早在 Python 3.4 中被引入。 它提供了比自 Python 3.0 起可用的 Protocol 3 更好的性能和更小的數據尺寸。

  • Removed one Py_ssize_t member from PyGC_Head. All GC tracked objects (e.g. tuple, list, dict) size is reduced 4 or 8 bytes. (Contributed by Inada Naoki in bpo-33597.)

  • uuid.UUID 現在會(huì )使用 __slots__ 以減少內存足跡。 (由 Wouter Bolsterlee 和 Tal Einat 在 bpo-30977 中貢獻。)

  • operator.itemgetter() 的性能提升了 33%。 優(yōu)化了參數處理,并為常見(jiàn)的在元組中單個(gè)非負整數索引的情況新增了一條快速路徑(這是標準庫中的典型用例)。 (由 Raymond Hettinger 在 bpo-35664 中貢獻。

  • 加快了在 collections.namedtuple() 中的字段查找。 它們現在的速度快了兩倍以上,成為 Python 中最快的實(shí)例變量查找形式。 (由 Raymond Hettinger, Pablo Galindo 和 Joe Jevnik, Serhiy Storchaka 在 bpo-32492 中貢獻。)

  • 如果輸入的可迭代對象的長(cháng)度已知 (即輸入對象實(shí)現了 __len__),list 構造器不會(huì )過(guò)度分配內部項緩沖區。 這使得所創(chuàng )建的列表資源占用平均減少了 12%。 (由 Raymond Hettinger 和 Pablo Galindo 在 bpo-33234 中貢獻。)

  • 類(lèi)變量寫(xiě)入速度加倍。 當一個(gè)非冗余屬性被更新時(shí),之前存在一個(gè)更新 slots 的非必要調用。 (由 Stefan Behnel, Pablo Galindo Salgado, Raymond Hettinger, Neil Schemenauer, 和 Serhiy Storchaka 在 bpo-36012 中貢獻。)

  • 減少了傳遞給許多內置函數和方法的參數轉換的開(kāi)銷(xiāo)。 這使得某些簡(jiǎn)單內置函數和方法的速度提升了 20--50%。 (由 Serhiy Storchaka 在 bpo-23867, bpo-35582bpo-36127 中貢獻。)

  • LOAD_GLOBAL 指令現在會(huì )使用新的 "per opcode cache" 機制。 它的速度現在提升了大約 40%。 (由 Yury Selivanov 和 Inada Naoki 在 bpo-26219 中貢獻。)

構建和 C API 的改變?

  • 默認的 sys.abiflags 成為一個(gè)空字符串:pymalloc 的 m 旗標不再有意義(無(wú)論是否啟用 pymalloc 構建都是兼容 ABI 的)因此已被移除。 (由 Victor Stinner 在 bpo-36707 中貢獻。)

    改變的例子:

    • 只會(huì )安裝 python3.8 程序,不再有 python3.8m 程序。

    • 只會(huì )安裝 python3.8-config 腳本,不再有 python3.8m-config 腳本。

    • m 旗標已經(jīng)從動(dòng)態(tài)庫文件名后綴中移除:包括標準庫中的擴展模塊以及第三方包所產(chǎn)生和安裝的模塊例如從 PyPI 下載的模塊。 以 Linux 為例,Python 3.7 的后綴 .cpython-37m-x86_64-linux-gnu.so 在 Python 3.8 中改為 .cpython-38-x86_64-linux-gnu.so。

  • 重新組織了所有頭文件以更好地區分不同種類(lèi)的 API:

    • Include/*.h 應為可移植的公有穩定版 C API。

    • Include/cpython/*.h 應為 CPython 專(zhuān)屬的不穩定版 C API;公有 API,部分私有 API 附加 _Py or _PY 前綴。

    • Include/internal/*.h 應為 CPython 特別專(zhuān)屬的私有內部 C API。 此 API 不具備向下兼容保證并且不應在 CPython 以外被使用。 它們的公開(kāi)僅適用于特別限定的需求例如調試器和性能分析等必須直接訪(fǎng)問(wèn) CPython 內部數據而不通過(guò)調用函數的應用。 此 API 現在是通過(guò) make install 安裝的。

    (由 Victor Stinner 在 bpo-35134bpo-35081 中貢獻,相關(guān)工作由 Eric Snow 在 Python 3.7 中發(fā)起。)

  • 某些宏已被轉換為靜態(tài)內聯(lián)函數:形參類(lèi)型和返回類(lèi)型定義良好,它們不再會(huì )有與宏相關(guān)的問(wèn)題,變量具有局部作用域。 例如:

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

  • PyByteArray_Init()PyByteArray_Fini() 函數已被移除。 它們自 Python 2.7.4 和 Python 3.2.0 起就沒(méi)有任何用處,被排除在受限 API (穩定版 ABI) 之外,并且未被寫(xiě)入文檔。 (由 Victor Stinner 在 bpo-35713 中貢獻。)

  • PyExceptionClass_Name() 的結果類(lèi)型現在是 const char * 而非 char *。 (由 Serhiy Storchaka 在 bpo-33818 中貢獻。)

  • Modules/Setup.distModules/Setup 兩者的共存已被移除。 之前在更新 CPython 源碼樹(shù)時(shí),開(kāi)發(fā)者必須手動(dòng)拷貝 Modules/Setup.dist (在源碼樹(shù)內) 到 Modules/Setup (在構建樹(shù)內) 以反映上游的任何改變。 舊特性對打包者來(lái)說(shuō)有一點(diǎn)益處,但代價(jià)是對追蹤 CPython 開(kāi)發(fā)進(jìn)程的開(kāi)發(fā)者造成經(jīng)常性的麻煩,因為忘記拷貝該文件可能導致構建失敗。

    現在構建系統總是會(huì )從源碼樹(shù)內的 Modules/Setup 讀取數據。 建議希望定制該文件的開(kāi)發(fā)者在 CPython 的一個(gè) git 分叉或補丁文件中維護他們的更改,就如他們對源碼樹(shù)做任何其他改變時(shí)一樣。

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

  • 將 Python 數字轉換為 C 整型的函數例如 PyLong_AsLong() 以及帶有 'i' 之類(lèi)整型轉換格式單元的參數解析函數例如 PyArg_ParseTuple() 現在如果可能將會(huì )使用 __index__() 特殊方法而不是 __int__()。 對于帶有 __int__() 方法但沒(méi)有 __index__() 方法的對象 (例如 DecimalFraction) 將會(huì )發(fā)出棄用警告。 對于實(shí)現了 __index__() 的對象 PyNumber_Check() 現在將返回 1。 PyNumber_Long(), PyNumber_Float()PyFloat_AsDouble() 現在如果可能也將會(huì )使用 __index__() 方法。 (由 Serhiy Storchaka 在 bpo-36048bpo-20092 中貢獻。)

  • 堆分配類(lèi)型對象現在將增加它們在 PyObject_Init() (及其對應的宏 PyObject_INIT) 中的引用計數而不是在 PyType_GenericAlloc() 中。 修改實(shí)例分配或中止分配的類(lèi)型可能需要進(jìn)行調整。 (由 Elizondo 在 bpo-35810 中貢獻。)

  • 新增函數 PyCode_NewWithPosOnlyArgs() 允許創(chuàng )建代碼對象例如 PyCode_New(),但帶有一個(gè)額外的 posonlyargcount 形參以指明僅限位置參數的數量。 (由 Pablo Galindo 在 bpo-37221 中貢獻。)

  • Py_SetPath() 現在會(huì )將 sys.executable 設為程序完整路徑 (Py_GetProgramFullPath()) 而不是程序名稱(chēng) (Py_GetProgramName())。 (由 Victor Stinner 在 bpo-38234 中貢獻。)

棄用?

API 與特性的移除?

下列特性與 API 已從 Python 3.8 中移除:

  • 自 Python 3.3 起,從 collections 導入 ABC 的做法已棄用,而應當從 collections.abc 執行導入。 從 collections 導入的操作在 3.8 中已標記為被移除,但將推遲到 3.9 中實(shí)施。 (參見(jiàn) bpo-36952。)

  • macpath 模塊,在 Python 3.7 中棄用,現已被移除。 (由 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 來(lái)幫助消除容易混淆 pyvenv 腳本所關(guān)聯(lián)的 Python 解釋器這一問(wèn)題。 (由 Brett Cannon 在 bpo-25427 中貢獻。)

  • parse_qs, parse_qslescape 已從 cgi 模塊中被移除。 這些函數自 Python 3.2 或更早就已被棄用。 它們應改為從 urllib.parsehtml 模塊導入。

  • filemode 函數已從 tarfile 模塊中被移除。 該函數未被寫(xiě)入文檔,自 Python 3.3 起就已棄用。

  • XMLParser 構造器不再接受 html 參數。 它從來(lái)沒(méi)有任何作用并在 Python 3.4 中已被棄用。 所有其他形參現在都是 僅限關(guān)鍵字參數。 (由 Serhiy Storchaka 在 bpo-29209 中貢獻。)

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

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

  • sqlite3 模塊的 CacheStatement 對象已不再公開(kāi)給用戶(hù)。 (由 Aviv Palivoda 在 bpo-30262 中貢獻。)

  • fileinput.input()fileinput.FileInput() 中自 Python 3.6 起就已被忽略并棄用的 bufsize 關(guān)鍵字參數已被移除。 由 Matthias Bussonnier 在 bpo-36952 中貢獻。)

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

移植到 Python 3.8?

本節列出了先前描述的更改以及可能需要更改代碼的其他錯誤修正.

Python 行為的改變?

  • yield 表達式(包括 yieldyield from 子句)現在不允許在推導式和生成器表達式中使用(但 for 子句最左邊的可迭代對象表達式除外)。 (由 Serhiy Storchaka 在 bpo-10544 中貢獻。)

  • 當標識號檢測 (isis not) 與特定類(lèi)型的字面值 (例如字符串和數字) 一同使用時(shí)編譯器現在會(huì )產(chǎn)生 SyntaxWarning。 這在 CPython 中通常是可行的,但并不被語(yǔ)言定義所保證。 該警告會(huì )建議用戶(hù)改用相等性檢測 (== and !=)。 (由 Serhiy Storchaka 在 bpo-34850 中貢獻。)

  • CPython 解釋器在某些情況下可以忽略異常。 在 Python 3.8 中這種情況會(huì )更少發(fā)生。 特別地,從類(lèi)型字典獲取屬性時(shí)引發(fā)的異常不會(huì )再被忽略。 (由 Serhiy Storchaka 在 bpo-35459 中貢獻。)

  • 從內置類(lèi)型 bool, int, float, complex 和標準庫的一些類(lèi)中移除了 __str__ 實(shí)現。 它們現在會(huì )從 object 繼承 __str__()。 作為結果,在這些類(lèi)的子類(lèi)中定義 __repr__() 方法將會(huì )影響它們的字符串表示。 (由 Serhiy Storchaka 在 bpo-36793 中貢獻。)

  • 在 AIX 上,sys.platform 將不再包含主要版本號。 它將總是 'aix' 而不再是 'aix3' .. 'aix7'。 由于較舊的 Python 版本會(huì )包含該版本號,因此推薦總是使用 sys.platform.startswith('aix')。 (由 M. Felt 在 bpo-36588 中貢獻。)

  • 現在 PyEval_AcquireLock()PyEval_AcquireThread() 如果當解釋器終結化時(shí)被調用將會(huì )終結當前線(xiàn)程,以使它們與 PyEval_RestoreThread(), Py_END_ALLOW_THREADS() 以及 PyGILState_Ensure() 保持一致。 如果不想要這樣的行為,請通過(guò)檢測 _Py_IsFinalizing()sys.is_finalizing() 來(lái)保護該調用。 (由 Joannah Nanjekye 在 bpo-36475 中貢獻。)

Python API 的變化?

  • 在 Windows 上 os.getcwdb() 函數現在會(huì )使用 UTF-8 編碼格式而不是 ANSI 代碼頁(yè):請參看 PEP 529 了解具體原因。 該函數在 Windows 上不再被棄用。 (由 Victor Stinner 在 bpo-37412 中貢獻。)

  • 現在 subprocess.Popen 在某些情況下會(huì )使用 os.posix_spawn() 以獲得更好的性能。 在適用于 Linux 的 Windows 子系統和 QEMU 用戶(hù)模擬器上,使用 os.posix_spawn()Popen 構造器不會(huì )再因為“找不到程序”這樣的錯誤引發(fā)異常。 而是讓子進(jìn)程失敗并返回一個(gè)非零的 returncode。 (由 Joannah Nanjekye 和 Victor Stinner 在 bpo-35537 中貢獻。)

  • The preexec_fn argument of * subprocess.Popen is no longer compatible with subinterpreters. The use of the parameter in a subinterpreter now raises RuntimeError. (Contributed by Eric Snow in bpo-34651, modified by Christian Heimes in bpo-37951.)

  • imap.IMAP4.logout() 方法不會(huì )再靜默地忽略任意異常。 (由 Victor Stinner 在 bpo-36348 中貢獻。)

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

  • 當傳入多模數據時(shí) statistics.mode() 函數不會(huì )再引發(fā)異常。 它將改為返回在輸入數據中遇到的第一個(gè)模式。 (由 Raymond Hettinger 在 bpo-35892 中貢獻。)

  • tkinter.ttk.Treeview 類(lèi)的 selection() 方法不再接受參數。 帶參數調用該方法來(lái)改變選擇在 Python 3.6 中已棄用。 請使用專(zhuān)門(mén)方法例如 selection_set() 來(lái)改變選擇。 (由 Serhiy Storchaka 在 bpo-31508 中貢獻。)

  • xml.dom.minidomwritexml(), toxml()toprettyxml() 方法以及 xml.etreewrite() 方法現在會(huì )保留用戶(hù)指定的屬性順序。 (由 Diego Rojas 和 Raymond Hettinger 在 bpo-34160 中貢獻。)

  • 附帶 'r' 旗標打開(kāi)的 dbm.dumb 數據庫現在將是只讀的。 如果數據庫不存在,附帶 'r''w' 旗標的 dbm.dumb.open() 不會(huì )再創(chuàng )建數據庫。 (由 Serhiy Storchaka 在 bpo-32749 中貢獻。)

  • XMLParser 的子類(lèi)中定義的 doctype() 方法將不會(huì )再被調用,并將導致發(fā)出 RuntimeWarning 而不是 DeprecationWarning。 請在目標上定義 doctype() 方法來(lái)處理 XML doctype 聲明。 (由 Serhiy Storchaka 在 bpo-29209 中貢獻。)

  • 現在當自定義元類(lèi)未在傳給 type.__new__ 的命名空間中提供 __classcell__ 入口時(shí)將引發(fā) RuntimeError。 在 Python 3.6--3.7 中是則是引發(fā) DeprecationWarning。 (由 Serhiy Storchaka 在 bpo-23722 中貢獻。)

  • cProfile.Profile 類(lèi)現在可被用作上下文管理器。 (由 Scott Sanderson 在 bpo-29235 中貢獻。)

  • shutil.copyfile(), shutil.copy(), shutil.copy2(), shutil.copytree()shutil.move() 會(huì )使用平臺專(zhuān)屬的 "fast-copy" 系統調用(參見(jiàn) 依賴(lài)于具體平臺的高效拷貝操作 一節)。

  • shutil.copyfile() 在 Windows 上的默認緩沖區大小從 16 KiB 改為 1 MiB。

  • PyGC_Head 結構已被完全改變。 所有接觸到該結構的代碼都應當被重寫(xiě)。 (參見(jiàn) bpo-33597。)

  • PyInterpreterState 結構已被移入 "internal" 頭文件(特別是 Include/internal/pycore_pystate.h)。 不透明的 PyInterpreterState 作為僅有 API(以及穩定版 ABI)的一部分仍然可用。 文檔指明該結構的任何字段都不是公有的,因此我們希望沒(méi)人在使用它們。 但是,如果你確實(shí)依賴(lài)其中某一個(gè)或更多個(gè)私有字段并且沒(méi)有其他替代選項,則請開(kāi)一個(gè) BPO 問(wèn)題。 我們將盡力幫助你進(jìn)行調整(可能包括向公有 API 添加訪(fǎng)問(wèn)器函數)。 (參見(jiàn) bpo-35886。)

  • 現在所有平臺下的 mmap.flush() 方法都會(huì )在成功時(shí)返回 None 并在錯誤時(shí)引發(fā)異常。 之前它的行為取決于具體平臺: Windows 下會(huì )在成功時(shí)返回非零值;在失敗時(shí)返回零。 Unix 下會(huì )在成功時(shí)返回零;在失敗時(shí)引發(fā)錯誤。 (由 Berker Peksag 在 bpo-2122 中貢獻。)

  • xml.dom.minidomxml.sax 模塊默認將不再處理外部實(shí)體。 (由 Christian Heimes 在 bpo-17239 中貢獻。)

  • 從只讀的 dbm 數據庫 (dbm.dumb, dbm.gnudbm.ndbm) 刪除鍵將會(huì )引發(fā) error (dbm.dumb.error, dbm.gnu.errordbm.ndbm.error) 而不是 KeyError。 (由 Xiang Zhang 在 bpo-33106 中貢獻。)

  • 簡(jiǎn)化了字面值的 AST。 所有常量將被表示為 ast.Constant 的實(shí)例。 實(shí)例化舊類(lèi) Num, Str, Bytes, NameConstantEllipsis 都將返回 Constant 的實(shí)例。 (由 Serhiy Storchaka 在 bpo-32892 中貢獻。)

  • expanduser() 在 Windows 上現在改用 USERPROFILE 環(huán)境變量而不再使用 HOME,后者通常不會(huì )為一般用戶(hù)賬戶(hù)設置。 (由 Anthony Sottile 在 bpo-36264 中貢獻。)

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

  • 當使用 asyncio.Task 的實(shí)例時(shí),函數 asyncio.wait_for() 現在會(huì )正確地等待撤銷(xiāo)。 在此之前當達到 timeout 時(shí),它會(huì )被撤銷(xiāo)并立即返回。 (由 Elvis Pranskevichus 在 bpo-32751 中貢獻。)

  • 當將 'socket' 作為 name 形參傳入時(shí),函數 asyncio.BaseTransport.get_extra_info() 現在會(huì )返回一個(gè)可安全使用的套接字對象。 (由 Yury Selivanov 在 bpo-37027 中貢獻。)

  • asyncio.BufferedProtocol 已經(jīng)晉級為穩定 API。

  • 在 Windows 上對擴展模塊的 DLL 依賴(lài)以及通過(guò) ctypes 加載的 DLL 的解析現在將更為安全。 只有系統路徑、包含相信 DLL 或 PYD 文件的路徑以及通過(guò) add_dll_directory() 添加的目錄才會(huì )被作為加載時(shí)依賴(lài)的搜索位置。 特別地,PATH 和當前工作目錄將不再被使用,對它們的修改將不再對正常的 DLL 解析產(chǎn)生影響。 如果你的應用依賴(lài)于這些機制,你應當先檢查 add_dll_directory(),如果它存在就用它在加載你的庫時(shí)添加你的 DLL 目錄。 請注意 Windows 7 用戶(hù)還需要確保 Windows 更新包 KB2533623 已安裝(這一點(diǎn)也會(huì )由安裝器進(jìn)行驗證)。 (由 Steve Dower 在 bpo-36085 中貢獻。)

  • 關(guān)聯(lián)到 pgen 的頭文件和函數在其被純 Python 實(shí)現取代后已被移除。 (由 Pablo Galindo 在 bpo-36623 中貢獻。)

  • types.CodeType 在構造器的第二個(gè)位置新增了一個(gè)形參 (posonlyargcount) 以支持在 PEP 570 中定義的僅限位置參數。 第一個(gè)參數 (argcount) 現在表示位置參數的總數量 (包括僅限位置參數)。 types.CodeType 中新增的 replace() 方法可用于讓代碼支持 future 特性。

C API 的變化?

  • PyCompilerFlags 結構體添加了一個(gè)新的 cf_feature_version 字段。 它應當被初始化為 PY_MINOR_VERSION。 該字段默認會(huì )被忽略,當且僅當 PyCF_ONLY_ASTcf_flags 中設置旗標時(shí)才會(huì )被使用。 (由 Guido van Rossum 在 bpo-35766 中貢獻。)

  • PyEval_ReInitThreads() 函數已從 C API 中移除。 它不應當被顯式地調用;請改用 PyOS_AfterFork_Child()。 (由 Victor Stinner 在 bpo-36728 中貢獻。)

  • 在 Unix 上,C 擴展不會(huì )再被鏈接到 libpython,但 Android 和 Cygwin 例外。 當 Python 被嵌入時(shí),libpython 不可使用 RTLD_LOCAL 加載,而要改用 RTLD_GLOBAL。 之前使用 RTLD_LOCAL 已經(jīng)不可能加載未鏈接到 libpython 的 C 擴展了,例如通過(guò) Modules/Setup*shared* 部分構建的標準庫 C 擴展。 (由 Victor Stinner 在 bpo-21536 中貢獻。)

  • 在解析或構建值時(shí)(例如 PyArg_ParseTuple(), Py_BuildValue(), PyObject_CallFunction() 等等)使用形如 # 的格式而不定義 PY_SSIZE_T_CLEAN 現在將會(huì )引發(fā) DeprecationWarning。 它將在 3.10 或 4.0 中被移除。 請參閱 解析參數并構建值變量 了解詳情。 (由 Inada Naoki 在 bpo-36381 中貢獻。)

  • 堆分配類(lèi)型的實(shí)例(例如使用 PyType_FromSpec() 創(chuàng )建的實(shí)例)會(huì )保存一個(gè)對其類(lèi)型對象的引用。 提升這些類(lèi)型對象引用計數的操作已從 PyType_GenericAlloc() 移至更低層級的函數 PyObject_Init()PyObject_INIT()。 這使用通過(guò)This makes types created through PyType_FromSpec() 所創(chuàng )建類(lèi)型的行為與管理代碼中的其他類(lèi)保持一致。

    靜態(tài)分配類(lèi)型 將不受影響。

    在大部分情況下,這應該都不會(huì )有附帶影響。 但是,在分配實(shí)例后手動(dòng)提升引用計數的類(lèi)型(也許是為了繞過(guò)漏洞)現在可能永遠不會(huì )被銷(xiāo)毀。 要避免這種情況,這些類(lèi)需要在實(shí)例撤銷(xiāo)分配期間在類(lèi)型對象上調用 Py_DECREF。

    要正確地將這些類(lèi)型移植到 3.8,請應用以下修改:

    • 在分配實(shí)例之后在類(lèi)型對象上移除 Py_INCREF —— 如果有的話(huà)。 這可以發(fā)生在調用 PyObject_New(), PyObject_NewVar(), PyObject_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;
      }
      
    • 確保所有堆分配類(lèi)型的自定義 tp_dealloc 函數會(huì )減少類(lèi)型的引用計數。

      示例:

      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() 宏已經(jīng)針對 MSVC 實(shí)現。 這個(gè)宏現在必須放在符號名稱(chēng)之前。

    示例:

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

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

  • 解釋器將不再假裝支持跨發(fā)布版本的擴展類(lèi)型二進(jìn)制兼容性。 由第三方擴展模塊所導出的 PyTypeObject 應該具有當前 Python 版本所要求的所有空位,包括 tp_finalize (Py_TPFLAGS_HAVE_FINALIZE 不會(huì )再在讀取 tp_finalize 之前被檢查)。

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

  • 函數 PyNode_AddChild()PyParser_AddToken() 現在接受兩個(gè)額外的 int 參數 end_linenoend_col_offset。

  • 允許 MinGW 工具直接鏈接到 python38.dlllibpython38.a 文件已不再包含于標準的 Windows 分發(fā)包中。 如果你需要此文件,可使用 gendefdlltool 工具來(lái)生成它,這些工具是 MinGW binutils 包的一部分:

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

    已安裝的 pythonXY.dll 所在位置將取決于安裝選項以及 Windows 的版本和語(yǔ)言。 請參閱 在Windows上使用 Python 了解更多信息。 該結果庫應當放在與 pythonXY.lib 相同的目錄下,這通常是你的 Python 安裝路徑下的 libs 目錄。

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

CPython 字節碼的改變?

  • 解釋器循環(huán)已通過(guò)將塊堆棧展開(kāi)邏輯移入編譯器獲得了簡(jiǎn)化。 編譯器現在會(huì )發(fā)出顯式指令來(lái)調整值堆棧并為 break, continuereturn 調用清除代碼。

    移除了操作碼 BREAK_LOOP, CONTINUE_LOOP, SETUP_LOOPSETUP_EXCEPT。 添加了新的操作碼 ROT_FOUR, BEGIN_FINALLY, CALL_FINALLYPOP_FINALLY。 修改了 END_FINALLYWITH_CLEANUP_START 的行為。

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

  • 添加了新的操作碼 END_ASYNC_FOR 用于處理當等待 async for 循環(huán)的下一項時(shí)引發(fā)的異常。 (由 Serhiy Storchaka 在 bpo-33041 中貢獻。)

  • MAP_ADD 現在會(huì )預期值為棧的第一個(gè)元素而鍵為第二個(gè)元素。 作出此改變以使得字典推導式能如 PEP 572 所提議的那樣,鍵總是會(huì )在值之前被求值。 (由 J?rn Heissler 在 bpo-35224 中貢獻。)

演示和工具?

添加了一個(gè)檢測腳本用于對訪(fǎng)問(wèn)變量的不同方式進(jìn)行計時(shí): Tools/scripts/var_access_benchmark.py。 (由 Raymond Hettinger 在 bpo-35884 中貢獻。)

以下是自 Python 3.3 以來(lái)性能提升情況的總結:

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

基準測試是在 Intel? Core? i7-4960HQ 處理器 上運行從 python.org 獲取的 macOS 64 位版得到的數據。 基準測試腳本顯示時(shí)間以納秒為單位。

Python 3.8.1 中的重要變化?

出于重要的安全性考量,asyncio.loop.create_datagram_endpoint()reuse_address 形參不再被支持。 這是由 UDP 中的套接字選項 SO_REUSEADDR 的行為導致的。 更多細節請參閱 loop.create_datagram_endpoint() 的文檔。 (由 Kyle Stanley, Antoine Pitrou 和 Yury Selivanov 在 bpo-37228 中貢獻。。)

Python 3.8.8 中的重要變化?

早先的 Python 版本允許使用 ;& 作為 urllib.parse.parse_qs()urllib.parse.parse_qsl() 中 query 形參的分隔鍵。 出于安全考慮,也為了遵循更新的 W3C 推薦設置,這已被改為只允許單個(gè)分隔鍵,默認為 &。 這一改變還會(huì )影響 cgi.parse()cgi.parse_multipart() 因為它們在內部使用了受影響的函數。 要了解更多細節,請查看它們各自的文檔。 (由 Adam Goldschmidt, Senthil Kumaran 和 Ken Jin 在 bpo-42967 中貢獻。)

Python 3.8.12 中的重要變化?

從 Python 3.8.12 開(kāi)始 ipaddress 模塊不再接受 IPv4 地址字符串中有任何前綴的零。 前綴的零有歧義且會(huì )被某些庫解讀為八進(jìn)制數字。 例如舊版函數 socket.inet_aton() 就將前綴的零視為八進(jìn)制數字。 最新 inet_pton() 的 glibc 實(shí)現則不接受任何前綴的零。

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