Python 3.11 中的新特性

编辑:

Pablo Galindo Salgado

本文解释了 Python 3.11 相较于 3.10 的新特性。Python 3.11 于 2022 年 10 月 24 日发布。有关完整详细信息,请参阅更新日志

摘要 – 版本亮点

  • Python 3.11 比 Python 3.10 快 10-60%。平均而言,我们在标准基准测试套件上测量了 1.25 倍的加速。有关详细信息,请参阅更快的 CPython

新的语法特性

新的内置特性

新的标准库模块

解释器改进

新的类型特性

重要的弃用、移除和限制

新特性

PEP 657:回溯中的精细错误位置

在打印回溯时,解释器现在将指向导致错误的精确表达式,而不仅仅是行。例如

Traceback (most recent call last):
  File "distance.py", line 11, in <module>
    print(manhattan_distance(p1, p2))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "distance.py", line 6, in manhattan_distance
    return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y)
                           ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'x'

以前版本的解释器只会指向行,使其不明确哪个对象为 None。当处理深度嵌套的 dict 对象和多个函数调用时,这些增强的错误也很有帮助

Traceback (most recent call last):
  File "query.py", line 37, in <module>
    magic_arithmetic('foo')
  File "query.py", line 18, in magic_arithmetic
    return add_counts(x) / 25
           ^^^^^^^^^^^^^
  File "query.py", line 24, in add_counts
    return 25 + query_user(user1) + query_user(user2)
                ^^^^^^^^^^^^^^^^^
  File "query.py", line 32, in query_user
    return 1 + query_count(db, response['a']['b']['c']['user'], retry=True)
                               ~~~~~~~~~~~~~~~~~~^^^^^
TypeError: 'NoneType' object is not subscriptable

以及复杂的算术表达式

Traceback (most recent call last):
  File "calculation.py", line 54, in <module>
    result = (x / y / z) * (a / b / c)
              ~~~~~~^~~
ZeroDivisionError: division by zero

此外,增强的回溯功能使用的信息可通过通用 API 获得,该 API 可用于将字节码 指令与源代码位置相关联。可以使用以下方法检索此信息

有关详细信息,请参阅PEP 657。(由 Pablo Galindo、Batuhan Taskaya 和 Ammar Askar 在 bpo-43950 中贡献。)

注意

此功能需要在代码对象中存储列位置,这可能会导致解释器内存使用量和已编译 Python 文件的磁盘使用量略有增加。要避免存储额外信息并停用打印额外回溯信息,请使用 -X no_debug_ranges 命令行选项或 PYTHONNODEBUGRANGES 环境变量。

PEP 654:异常组和 except*

PEP 654 引入了语言特性,使程序能够同时引发和处理多个不相关的异常。内置类型 ExceptionGroupBaseExceptionGroup 使分组异常并将其一起引发成为可能,新的 except* 语法概括了 except 以匹配异常组的子组。

有关详细信息,请参阅PEP 654

(由 Irit Katriel 在 bpo-45292 中贡献。PEP 由 Irit Katriel、Yury Selivanov 和 Guido van Rossum 撰写。)

PEP 678:可以使用注释来丰富异常

add_note() 方法被添加到 BaseException。它可用于使用在引发异常时不可用的上下文信息来丰富异常。添加的注释将出现在默认的回溯中。

有关详细信息,请参阅PEP 678

(由 Irit Katriel 在 bpo-45607 中贡献。PEP 由 Zac Hatfield-Dodds 撰写。)

Windows py.exe 启动器改进

Python 3.11 中包含的 Windows 版 Python 启动器的副本已得到显著更新。它现在支持 PEP 514 中定义的公司/标签语法,使用 -V:<公司>/<标签> 参数而不是有限的 -<主版本>.<次版本>。这允许启动 PythonCore(托管在 python.org 上的)以外的发行版。

当使用 -V: 选择器时,公司或标签可以省略,但会搜索所有安装。例如,-V:OtherPython/ 将选择为 OtherPython 注册的“最佳”标签,而 -V:3.11-V:/3.11 将选择带有标签 3.11 的“最佳”发行版。

当使用旧的 -<major>-<major>.<minor>-<major>-<bitness>-<major>.<minor>-<bitness> 参数时,应保留过去版本的所有现有行为,并且仅选择来自 PythonCore 的发行版。但是,-64 后缀现在表示“不是 32 位”(不一定是 x86-64),因为有多个支持的 64 位平台。通过检查运行时的标签是否有 -32 后缀来检测 32 位运行时。自 3.5 以来的所有 Python 版本都在其 32 位构建中包含了此后缀。

其他语言变更

  • 添加了一个 -P 命令行选项和一个 PYTHONSAFEPATH 环境变量,它们在运行脚本时禁用自动将脚本目录前置到 sys.path,或者在使用 -c-m 时禁用自动将当前目录前置到 sys.path。这确保只有 stdlib 和已安装的模块被 import 拾取,并避免无意或恶意地使用本地(通常是用户可写的)目录中的模块来覆盖模块。(由 Victor Stinner 在 gh-57684 中贡献。)

  • 格式规范迷你语言 中添加了一个 "z" 选项,该选项在舍入到格式精度后将负数强制转换为正零。有关更多详细信息,请参阅 PEP 682。(由 John Belmonte 在 gh-90153 中贡献。)

  • 不再接受 sys.path 上的字节。支持在 Python 3.2 和 3.6 之间的某个时间点中断,直到 Python 3.10.0 发布后才有人注意到。此外,由于当存在 strbytes 键混合时,-bsys.path_importer_cache 之间的交互,恢复支持会很麻烦。(由 Thomas Grainger 在 gh-91181 中贡献。)

其他 CPython 实现更改

  • 实现了 complex 的特殊方法 __complex__()bytes 的特殊方法 __bytes__(),以支持 typing.SupportsComplextyping.SupportsBytes 协议。(由 Mark Dickinson 和 Donghee Na 在 bpo-24234 中贡献。)

  • 添加了 siphash13 作为新的内部哈希算法。它具有与 siphash24 相似的安全属性,但对于长输入来说速度稍快。strbytes 和一些其他类型现在使用它作为 hash() 的默认算法。PEP 552 基于哈希值的 .pyc 文件 现在也使用 siphash13。(由 Inada Naoki 在 bpo-29410 中贡献。)

  • 当使用不带参数的 raise 语句重新引发活动异常时,附加到此异常的回溯现在始终为 sys.exc_info()[1].__traceback__。这意味着在当前的 except 子句中对回溯所做的更改将反映在重新引发的异常中。(由 Irit Katriel 在 bpo-45711 中贡献。)

  • 解释器状态对已处理异常的表示(又名 exc_info_PyErr_StackItem)现在只有 exc_value 字段;exc_typeexc_traceback 已被删除,因为它们可以从 exc_value 推导出来。(由 Irit Katriel 在 bpo-45711 中贡献。)

  • 为 Windows 安装程序添加了一个新的 命令行选项AppendPath。它的行为与 PrependPath 类似,但它是追加而不是前置安装目录和脚本目录。(由 Bastian Neuburger 在 bpo-44934 中贡献。)

  • 现在必须将 PyConfig.module_search_paths_set 字段设置为 1,以便初始化使用 PyConfig.module_search_paths 来初始化 sys.path。否则,初始化将重新计算路径并替换添加到 module_search_paths 的任何值。

  • 现在,--help 选项的输出符合 50 行/80 列。关于 Python 环境变量-X 选项的信息现在可以使用相应的 --help-env--help-xoptions 标志获得,并且可以使用新的 --help-all 标志获得。(由 Éric Araujo 在 bpo-46142 中贡献。)

  • 在 2(二进制)、4、8(八进制)、16(十六进制)或 32 之外的进制中,将 intstr 之间进行转换时,如果字符串形式的数字位数超过限制,现在会引发 ValueError,以避免由于算法复杂度而导致的潜在拒绝服务攻击。这是对 CVE 2020-10735 的缓解措施。此限制可以通过环境变量、命令行标志或 sys API 进行配置或禁用。请参阅 整数字符串转换长度限制 文档。字符串形式的默认限制是 4300 位数字。

新模块

改进的模块

asyncio

contextlib

  • 添加了非并行安全的 chdir() 上下文管理器,用于更改当前工作目录,然后在退出时恢复它。 简单的 chdir() 包装器。(由 Filipe Laíns 在 bpo-25625 中贡献)

dataclasses

  • 更改字段默认可变性检查,仅允许默认值是 可哈希的,而不是任何不是 dictlistset 实例的对象。(由 Eric V. Smith 在 bpo-44674 中贡献。)

datetime

enum

  • EnumMeta 重命名为 EnumType(保留 EnumMeta 作为别名)。

  • 添加了 StrEnum,其成员可以用作(并且必须是)字符串。

  • 添加了 ReprEnum,它只修改成员的 __repr__(),同时为其 __str__()__format__()(由 str()format()f-string 使用)返回其字面量值(而不是名称)。

  • 更改了 Enum.__format__()format()str.format()f-string 的默认值),使其始终产生与 Enum.__str__() 相同的结果:对于继承自 ReprEnum 的枚举,它将是成员的值;对于所有其他枚举,它将是枚举和成员名称(例如, Color.RED)。

  • Flag 枚举和 FlagBoundary 枚举及其选项添加了一个新的 boundary 类参数,用于控制如何处理超出范围的标志值。

  • 添加了 verify() 枚举装饰器和 EnumCheck 枚举及其选项,以针对多个特定约束检查枚举类。

  • 添加了 member()nonmember() 装饰器,以确保被装饰的对象被/不被转换为枚举成员。

  • 添加了 property() 装饰器,它的工作方式类似于 property(),但用于枚举。请使用它来代替 types.DynamicClassAttribute()

  • 添加了 global_enum() 枚举装饰器,它会调整 __repr__()__str__() 以将值显示为它们模块的成员,而不是枚举类的成员。例如,对于 re.RegexFlagASCII 成员,显示 're.ASCII' 而不是 'RegexFlag.ASCII'

  • 增强了 Flag,使其支持在其成员上使用 len()、迭代以及 in/not in。例如,现在以下代码可以正常工作:len(AFlag(3)) == 2 and list(AFlag(3)) == (AFlag.ONE, AFlag.TWO)

  • 更改了 EnumFlag,以便成员现在在调用 __init_subclass__() 之前定义;dir() 现在包括来自混合数据类型的方法等。

  • 更改了 Flag,使其仅将主值(2 的幂)视为规范值,而将复合值(3610 等)视为别名;反转标志被强制转换为其正等效值。

fcntl

  • 在 FreeBSD 上,支持 F_DUP2FDF_DUP2FD_CLOEXEC 标志,前者等于 dup2 的用法,而后者则额外设置了 FD_CLOEXEC 标志。

fractions

  • 支持从字符串初始化 FractionPEP 515 样式。(由 Sergey B Kirpichev 在 bpo-44258 中贡献。)

  • Fraction 现在实现了一个 __int__ 方法,因此 isinstance(some_fraction, typing.SupportsInt) 检查会通过。(由 Mark Dickinson 在 bpo-44547 中贡献。)

functools

  • functools.singledispatch() 现在支持将 types.UnionTypetyping.Union 作为调度参数的注释。

    >>> from functools import singledispatch
    >>> @singledispatch
    ... def fun(arg, verbose=False):
    ...     if verbose:
    ...         print("Let me just say,", end=" ")
    ...     print(arg)
    ...
    >>> @fun.register
    ... def _(arg: int | float, verbose=False):
    ...     if verbose:
    ...         print("Strength in numbers, eh?", end=" ")
    ...     print(arg)
    ...
    >>> from typing import Union
    >>> @fun.register
    ... def _(arg: Union[list, set], verbose=False):
    ...     if verbose:
    ...         print("Enumerate this:")
    ...     for i, elem in enumerate(arg):
    ...         print(i, elem)
    ...
    

    (由 Yurii Karabas 在 bpo-46014 中贡献。)

gzip

  • 当使用 mtime=0 参数时,gzip.compress() 函数现在更快,因为它将压缩完全委托给单个 zlib.compress() 操作。此更改有一个副作用:gzip 文件头在其头部包含一个“OS”字节。传统上,gzip 模块始终将其设置为值 255,表示“未知”。现在,当使用 mtime=0compress() 时,它可能会被 Python 链接到的底层 zlib C 库设置为不同的值。(有关副作用的详细信息,请参阅 gh-112346。)

hashlib

  • hashlib.blake2b()hashlib.blake2s() 现在首选 libb2 而不是 Python 的供应商副本。(由 Christian Heimes 在 bpo-47095 中贡献。)

  • 带有 SHA3 和 SHAKE 算法的内部 _sha3 模块现在使用 tiny_sha3 而不是 Keccak Code Package 来减少代码和二进制大小。hashlib 模块首选来自 OpenSSL 的优化 SHA3 和 SHAKE 实现。此更改仅影响没有 OpenSSL 支持的安装。(由 Christian Heimes 在 bpo-47098 中贡献。)

  • 添加了 hashlib.file_digest(),这是一个用于有效哈希文件或类文件对象的辅助函数。(由 Christian Heimes 在 gh-89313 中贡献。)

IDLE 和 idlelib

  • 将语法高亮应用于 .pyi 文件。(由 Alex Waygood 和 Terry Jan Reedy 在 bpo-45447 中贡献。)

  • 在保存包含输入和输出的 Shell 时包含提示。(由 Terry Jan Reedy 在 gh-95191 中贡献。)

inspect

locale

logging

math

  • 添加 math.exp2():返回 2 的 x 次幂。(由 Gideon Mitchell 在 bpo-45917 中贡献。)

  • 添加 math.cbrt():返回 x 的立方根。(由 Ajith Ramachandran 在 bpo-44357 中贡献。)

  • 为了与 IEEE 754 规范保持一致,更改了 math.pow() 的两个极端情况的行为。math.pow(0.0, -math.inf)math.pow(-0.0, -math.inf) 操作现在返回 inf。以前它们会引发 ValueError。(由 Mark Dickinson 在 bpo-44339 中贡献。)

  • math.nan 值现在始终可用。(由 Victor Stinner 在 bpo-46917 中贡献。)

operator

  • 添加了一个新函数 operator.call,使得 operator.call(obj, *args, **kwargs) == obj(*args, **kwargs)。(由 Antony Lee 在 bpo-44019 中贡献。)

os

  • 在 Windows 上,os.urandom() 现在使用 BCryptGenRandom(),而不是已弃用的 CryptGenRandom()。(由 Donghee Na 在 bpo-44611 中贡献。)

pathlib

re

  • 正则表达式现在支持原子分组 ((?>...)) 和占有量词 (*+++?+{m,n}+)。(由 Jeffrey C. Jacobs 和 Serhiy Storchaka 在 bpo-433030 中贡献。)

shutil

socket

  • 为 NetBSD 添加 CAN 套接字支持。(由 Thomas Klausner 在 bpo-30512 中贡献。)

  • 如果连接失败,create_connection() 有一个选项可以引发一个包含所有错误的 ExceptionGroup,而不是只引发最后一个错误。(由 Irit Katriel 在 bpo-29980 中贡献。)

sqlite3

string

sys

  • sys.exc_info() 现在从 value (异常实例) 中派生出 typetraceback 字段,因此当异常在处理过程中被修改时,对 exc_info() 的后续调用结果会反映这些更改。(由 Irit Katriel 在 bpo-45711 中贡献。)

  • 添加 sys.exception(),它返回当前活动的异常实例(等效于 sys.exc_info()[1])。(由 Irit Katriel 在 bpo-46328 中贡献。)

  • 添加 sys.flags.safe_path 标志。(由 Victor Stinner 在 gh-57684 中贡献。)

sysconfig

  • 添加了三种新的 安装方案posix_venvnt_venvvenv),当 Python 创建新的虚拟环境或从虚拟环境中运行时使用。前两种方案(posix_venvnt_venv)是特定于操作系统的,分别用于非 Windows 和 Windows,venv 本质上是根据 Python 运行的操作系统对其中一种方案的别名。这对于修改 sysconfig.get_preferred_scheme() 的下游发行商很有用。创建新虚拟环境的第三方代码应使用新的 venv 安装方案来确定路径,就像 venv 一样。(由 Miro Hrončok 在 bpo-45413 中贡献。)

tempfile

threading

time

  • 在 Unix 上,time.sleep() 现在使用 clock_nanosleep()nanosleep() 函数(如果可用),其分辨率为 1 纳秒(10-9 秒),而不是使用分辨率为 1 微秒(10-6 秒)的 select()。(由 Benjamin Szőke 和 Victor Stinner 在 bpo-21302 中贡献。)

  • 在 Windows 8.1 及更高版本上,time.sleep() 现在使用基于 高分辨率定时器的可等待计时器,其分辨率为 100 纳秒(10-7 秒)。以前,它的分辨率为 1 毫秒(10-3 秒)。(由 Benjamin Szőke、Donghee Na、Eryk Sun 和 Victor Stinner 在 bpo-21302bpo-45429 中贡献。)

tkinter

  • 添加了方法 info_patchlevel(),它返回 Tcl 库的精确版本,作为一个类似于 sys.version_info 的命名元组。(由 Serhiy Storchaka 在 gh-91827 中贡献。)

traceback

typing

有关重大更改,请参阅 与类型提示相关的新功能

  • 添加 typing.assert_never()typing.Nevertyping.assert_never() 对于要求类型检查器确认一行代码不可访问很有用。在运行时,它会引发 AssertionError。(由 Jelle Zijlstra 在 gh-90633 中贡献。)

  • 添加 typing.reveal_type()。这对于询问类型检查器它为给定的表达式推断了什么类型很有用。在运行时,它会打印接收到的值的类型。(由 Jelle Zijlstra 在 gh-90572 中贡献。)

  • 添加了 typing.assert_type()。 此方法用于要求类型检查器确认其推断出的给定表达式的类型与给定的类型是否匹配。在运行时,它只返回接收到的值。(由 Jelle Zijlstra 在 gh-90638 中贡献。)

  • typing.TypedDict 类型现在可以是泛型的。(由 Samodya Abeysiriwardane 在 gh-89026 中贡献。)

  • NamedTuple 类型现在可以是泛型的。(由 Serhiy Storchaka 在 bpo-43923 中贡献。)

  • 允许 typing.Any 的子类化。 这有助于避免与高度动态的类(例如 mocks)相关的类型检查器错误。(由 Shantanu Jain 在 gh-91154 中贡献。)

  • typing.final() 装饰器现在在被装饰的对象上设置 __final__ 属性。(由 Jelle Zijlstra 在 gh-90500 中贡献。)

  • typing.get_overloads() 函数可用于内省函数的重载。typing.clear_overloads() 可用于清除函数的所有已注册重载。(由 Jelle Zijlstra 在 gh-89263 中贡献。)

  • __init__() 方法现在保留 Protocol 子类。(由 Adrian Garcia Badarasco 在 gh-88970 中贡献。)

  • 空元组类型(Tuple[()])的表示形式已简化。 这会影响内省,例如,get_args(Tuple[()]) 现在求值为 () 而不是 ((),)。(由 Serhiy Storchaka 在 gh-91137 中贡献。)

  • 通过删除私有 typing._type_check 函数中的可调用检查,放宽了类型注释的运行时要求。(由 Gregory Beauregard 在 gh-90802 中贡献。)

  • typing.get_type_hints() 现在支持在 PEP 585 泛型别名 中将字符串评估为前向引用。(由 Niklas Rosenstein 在 gh-85542 中贡献。)

  • typing.get_type_hints() 不再将 Optional 添加到默认值为 None 的参数。(由 Nikita Sobolev 在 gh-90353 中贡献。)

  • typing.get_type_hints() 现在支持评估裸字符串化的 ClassVar 注释。(由 Gregory Beauregard 在 gh-90711 中贡献。)

  • typing.no_type_check() 不再修改外部类和函数。 它现在还正确地将 classmethod 标记为不进行类型检查。(由 Nikita Sobolev 在 gh-90729 中贡献。)

unicodedata

  • Unicode 数据库已更新至 14.0.0 版本。(由 Benjamin Peterson 在 bpo-45190 中贡献。)

unittest

venv

  • 创建新的 Python 虚拟环境时,venv sysconfig 安装方案 用于确定环境内的路径。 当 Python 在虚拟环境中运行时,相同的安装方案是默认方案。 这意味着下游分发商可以更改默认的 sysconfig 安装方案,而无需更改虚拟环境的行为。 创建新虚拟环境的第三方代码也应执行相同的操作。(由 Miro Hrončok 在 bpo-45413 中贡献。)

warnings

zipfile

  • 添加了对指定成员名称编码的支持,用于在 ZipFile 的目录和文件标头中读取元数据。(由 Stephen J. Turnbull 和 Serhiy Storchaka 在 bpo-28080 中贡献。)

  • 添加了 ZipFile.mkdir(),用于在 ZIP 存档中创建新目录。(由 Sam Ezeh 在 gh-49083 中贡献。)

  • zipfile.Path 添加了 stemsuffixsuffixes。(由 Miguel Brito 在 gh-88261 中贡献。)

优化

本节介绍独立于 Faster CPython 项目的特定优化,该项目将在其自己的部分中介绍。

  • 编译器现在优化仅包含格式代码 %s%r%a 的字符串字面量上的简单 printf 样式 % 格式化,并使其与相应的 f-string 表达式一样快。(由 Serhiy Storchaka 在 bpo-28307 中贡献。)

  • 整数除法(//)更适合编译器进行优化。当使用小于 2**30 的值去除一个 int 时,在 x86-64 架构上速度现在提升了约 20%。(由 Gregory P. Smith 和 Tim Peters 在 gh-90564 中贡献。)

  • sum() 函数现在对于小于 2**30 的整数快了近 30%。(由 Stefan Behnel 在 gh-68264 中贡献。)

  • 列表调整大小针对常见情况进行了优化,使 list.append() 的速度提高了约 15%,简单的列表推导式的速度提高了 20-30% (由 Dennis Sweeney 在 gh-91165 中贡献。)

  • 当所有键都是 Unicode 对象时,字典不再存储哈希值,从而减小了 dict 的大小。例如,在 64 位平台上,sys.getsizeof(dict.fromkeys("abcdefg")) 从 352 字节减少到 272 字节(缩小了 23%)。(由 Inada Naoki 在 bpo-46845 中贡献。)

  • 当通过 UDP 传输大文件时,使用 asyncio.DatagramProtocol 的速度现在提升了几个数量级,对于大约 60 MiB 的文件,速度提高了 100 倍以上。(由 msoxzw 在 gh-91487 中贡献。)

  • math 模块中的 comb()perm() 函数对于大参数的速度现在快了约 10 倍(对于较大的 k,加速效果更明显)。(由 Serhiy Storchaka 在 bpo-37295 中贡献。)

  • statistics 模块中的 mean()variance()stdev() 函数现在以一次遍历的方式处理迭代器,而不是先将其转换为 list。 这速度快了两倍,并且可以节省大量内存。(由 Raymond Hettinger 在 gh-90415 中贡献。)

  • unicodedata.normalize() 现在可以在恒定时间内规范化纯 ASCII 字符串。(由 Donghee Na 在 bpo-44987 中贡献。)

更快的 CPython

使用 Ubuntu Linux 上的 GCC 编译时,CPython 3.11 比 CPython 3.10 平均快 25%,这是通过 pyperformance 基准测试套件测量的。 根据您的工作负载,整体加速可能在 10-60% 之间。

此项目侧重于 Python 中的两个主要领域:更快的启动更快的运行时。 此项目未涵盖的优化单独列在 优化 下。

更快的启动

冻结导入/静态代码对象

Python 在 __pycache__ 目录中缓存字节码,以加快模块加载速度。

在之前的 3.10 版本中,Python 模块执行如下所示

Read __pycache__ -> Unmarshal -> Heap allocated code object -> Evaluate

在 Python 3.11 中,Python 启动所必需的核心模块是“冻结的”。 这意味着它们的 代码对象(和字节码)由解释器静态分配。 这将模块执行过程中的步骤减少为

Statically allocated code object -> Evaluate

Python 3.11 中解释器启动速度现在快了 10-15%。 这对于使用 Python 的短时间运行程序有很大的影响。

(由 Eric Snow、Guido van Rossum 和 Kumar Aditya 在许多问题中贡献。)

更快的运行时

更便宜、延迟的 Python 帧

每当 Python 调用 Python 函数时,都会创建保存执行信息的 Python 帧。 以下是新的帧优化

  • 简化了帧创建过程。

  • 通过在 C 堆栈上大量重用帧空间来避免内存分配。

  • 简化了内部帧结构,使其仅包含必要的信息。 帧以前包含额外的调试和内存管理信息。

只有当调试器或 Python 内省函数(如 sys._getframe()inspect.currentframe())请求时,才会创建旧式帧对象。 对于大多数用户代码,根本不会创建帧对象。 因此,几乎所有 Python 函数调用都得到了显著加速。 我们在 pyperformance 中测量到了 3-7% 的加速。

(由 Mark Shannon 在 bpo-44590 中贡献。)

内联 Python 函数调用

在 Python 函数调用期间,Python 将调用一个评估 C 函数来解释该函数的代码。 这实际上将纯 Python 递归限制为 C 堆栈安全范围内的递归。

在 3.11 中,当 CPython 检测到 Python 代码调用另一个 Python 函数时,它会设置一个新帧,并“跳转”到新帧内的新代码。 这避免了完全调用 C 解释函数。

现在,大多数 Python 函数调用都不占用 C 堆栈空间,从而加快了速度。 在像 fibonacci 或阶乘这样的简单递归函数中,我们观察到了 1.7 倍的加速。 这也意味着递归函数可以递归得更深(如果用户使用 sys.setrecursionlimit() 增加递归限制)。 我们在 pyperformance 中测量到了 1-3% 的改进。

(由 Pablo Galindo 和 Mark Shannon 在 bpo-45256 中贡献。)

PEP 659:专门化的自适应解释器

PEP 659 是 Faster CPython 项目的关键组成部分之一。 其总体思想是,虽然 Python 是一种动态语言,但大多数代码都存在对象和类型很少更改的区域。 此概念称为类型稳定性

在运行时,Python 将尝试查找正在执行的代码中的常见模式和类型稳定性。 然后,Python 会将当前操作替换为更专门化的操作。 这种专门化的操作使用仅适用于这些用例/类型的快速路径,通常其性能优于通用操作。 这也引入了另一个称为内联缓存的概念,其中 Python 直接在 字节码中缓存开销较大的操作的结果。

特殊化程序还将某些常见的指令对组合成一个超级指令,从而减少执行期间的开销。

Python 仅在看到“热”(多次执行)的代码时才进行特殊化。 这可以防止 Python 在只运行一次的代码上浪费时间。 当代码过于动态或使用方式发生变化时,Python 也可以取消特殊化。 会定期尝试特殊化,并且特殊化尝试的开销并不太大,从而允许特殊化适应新的情况。

(PEP 由 Mark Shannon 编写,其思想受到 Stefan Brunthaler 的启发。 请参阅PEP 659 获取更多信息。 由 Mark Shannon 和 Brandt Bucher 实现,并得到了 Irit Katriel 和 Dennis Sweeney 的额外帮助。)

操作

形式

特殊化

操作加速(高达)

贡献者

二元运算

x + x

x - x

x * x

常见的类型(如 intfloatstr)的二元加法、乘法和减法为其底层类型采用了自定义的快速路径。

10%

Mark Shannon、Donghee Na、Brandt Bucher、Dennis Sweeney

下标

a[i]

对诸如 listtupledict 等容器类型进行下标操作时,会直接索引底层数据结构。

自定义的 __getitem__() 的下标操作也会以内联方式进行,类似于 内联 Python 函数调用

10-25%

Irit Katriel, Mark Shannon

存储下标

a[i] = z

类似于上面的下标操作特化。

10-25%

Dennis Sweeney

调用

f(arg)

C(arg)

对诸如 len()str 等常用内置 (C) 函数和类型的调用会直接调用其底层的 C 版本。这避免了通过内部调用约定进行调用。

20%

Mark Shannon, Ken Jin

加载全局变量

print

len

对象在全局/内置命名空间中的索引会被缓存。加载全局变量和内置变量无需进行命名空间查找。

[1]

Mark Shannon

加载属性

o.attr

类似于加载全局变量。属性在类/对象命名空间中的索引会被缓存。在大多数情况下,加载属性将不需要进行命名空间查找。

[2]

Mark Shannon

加载用于调用的方法

o.meth()

方法的实际地址会被缓存。现在加载方法无需进行命名空间查找,即使对于具有很长继承链的类也是如此。

10-20%

Ken Jin, Mark Shannon

存储属性

o.attr = z

类似于加载属性的优化。

在 pyperformance 中占 2%

Mark Shannon

解包序列

*seq

针对诸如 listtuple 等常用容器进行了特化。避免了内部调用约定。

8%

Brandt Bucher

其他

  • 由于延迟创建的对象命名空间,现在对象需要的内存更少。它们的命名空间字典现在也可以更自由地共享键。(由 Mark Shannon 在 bpo-45340bpo-40116 中贡献。)

  • 实现了“零成本”异常,消除了在未引发异常时 try 语句的开销。(由 Mark Shannon 在 bpo-40222 中贡献。)

  • 解释器中更简洁的异常表示形式将捕获异常所需的时间减少了约 10%。(由 Irit Katriel 在 bpo-45711 中贡献。)

  • re 的正则表达式匹配引擎已被部分重构,现在在支持的平台上使用计算的 goto(或“线程代码”)。因此,Python 3.11 执行 pyperformance 正则表达式基准测试的速度比 Python 3.10 快 10%。 (由 Brandt Bucher 在 gh-91404 中贡献。)

常见问题解答

我应该如何编写代码来利用这些加速?

编写遵循常见最佳实践的 Pythonic 代码;你无需更改代码。Faster CPython 项目针对我们观察到的常见代码模式进行优化。

CPython 3.11 会使用更多内存吗?

可能不会;我们预计内存使用量不会比 3.10 高出 20%。这可以通过上述针对帧对象和对象字典的内存优化来抵消。

我没有在我的工作负载中看到任何加速。为什么?

某些代码不会有明显的收益。如果你的代码大部分时间都花在 I/O 操作上,或者已经在像 NumPy 这样的 C 扩展库中完成了大部分计算,那么就不会有显著的加速。此项目目前对纯 Python 工作负载的收益最大。

此外,pyperformance 数据是几何平均值。即使在 pyperformance 基准测试中,某些基准测试也略有放缓,而另一些基准测试则加速了近 2 倍!

有 JIT 编译器吗?

没有。我们仍在探索其他优化。

关于

Faster CPython 探索 CPython 的优化。主要团队由 Microsoft 资助全职从事这项工作。Pablo Galindo Salgado 也由 Bloomberg LP 资助兼职从事该项目。最后,许多贡献者是来自社区的志愿者。

CPython 字节码更改

字节码现在包含内联缓存条目,其形式为新添加的 CACHE 指令。许多操作码都期望后跟确切数量的缓存,并指示解释器在运行时跳过它们。填充的缓存看起来可能像任意指令,因此在读取或修改包含加速数据的原始自适应字节码时应格外小心。

新操作码

替换的操作码

替换的操作码

新的操作码

注释

BINARY_*
INPLACE_*

BINARY_OP

将所有数值二进制/就地操作码替换为单个操作码

CALL_FUNCTION
CALL_FUNCTION_KW
CALL_METHOD
KW_NAMES
PRECALL

将方法的参数移位与关键字参数的处理解耦;允许更好地特化调用

DUP_TOP
DUP_TOP_TWO
ROT_TWO
ROT_THREE
ROT_FOUR
ROT_N

堆栈操作指令

JUMP_IF_NOT_EXC_MATCH

现在执行检查但不跳转

JUMP_ABSOLUTE
POP_JUMP_IF_FALSE
POP_JUMP_IF_TRUE
POP_JUMP_BACKWARD_IF_*
POP_JUMP_FORWARD_IF_*

请参阅[3];每个方向的 TRUEFALSENONENOT_NONE 变体

SETUP_WITH
SETUP_ASYNC_WITH

BEFORE_WITH

with 块设置

已更改/删除的操作码

  • 更改了 MATCH_CLASSMATCH_KEYS,使其不再推送额外的布尔值来指示成功/失败。相反,如果失败,则会推送 None 来代替提取的值的元组。

  • 更改了与异常一起使用的操作码,以反映它们现在在堆栈上表示为一个项目,而不是三个(请参阅 gh-89874)。

  • 删除了 COPY_DICT_WITHOUT_KEYSGEN_STARTPOP_BLOCKSETUP_FINALLYYIELD_FROM

已弃用

本节列出了 Python 3.11 中已弃用的 Python API。

已弃用的 C API 将单独列出

语言/内置函数

  • 链式 classmethod 描述符(在 bpo-19072 中引入)现已弃用。它不再能用于包装其他描述符,例如 property。此功能的核心设计存在缺陷,并导致了许多下游问题。要“传递”一个 classmethod,请考虑使用 Python 3.10 中添加的 __wrapped__ 属性。(由 Raymond Hettinger 在 gh-89519 中贡献。)

  • 在字符串和字节字面量中使用大于 0o377 (十进制的 255) 的八进制转义现在会产生一个 DeprecationWarning。在未来的 Python 版本中,它们将引发一个 SyntaxWarning,并最终引发一个 SyntaxError。(由 Serhiy Storchaka 在 gh-81548 中贡献。)

  • 现在已弃用将 int() 委托给 __trunc__()。当 type(a) 实现 __trunc__() 但没有实现 __int__()__index__() 时调用 int(a) 现在会引发一个 DeprecationWarning。(由 Zackery Spytz 在 bpo-44977 中贡献。)

模块

  • PEP 594 导致以下模块被弃用,计划在 Python 3.13 中移除

    aifc

    chunk

    msilib

    pipes

    telnetlib

    audioop

    crypt

    nis

    sndhdr

    uu

    cgi

    imghdr

    nntplib

    spwd

    xdrlib

    cgitb

    mailcap

    ossaudiodev

    sunau

    (由 Brett Cannon 在 bpo-47061 中贡献,Victor Stinner 在 gh-68966 中贡献。)

  • asynchatasyncoresmtpd 模块至少自 Python 3.6 以来就被弃用了。它们的文档和弃用警告现在已更新,以说明它们将在 Python 3.12 中移除。(由 Hugo van Kemenade 在 bpo-47022 中贡献。)

  • lib2to3 包和 2to3 工具现在已弃用,可能无法解析 Python 3.10 或更高版本。有关详细信息,请参阅引入新 PEG 解析器的 PEP 617。(由 Victor Stinner 在 bpo-40360 中贡献。)

  • 未文档化的模块 sre_compilesre_constantssre_parse 现在已弃用。(由 Serhiy Storchaka 在 bpo-47152 中贡献。)

标准库

  • 自 Python 3.2 以来,以下内容在 configparser 中已弃用。它们的弃用警告现在已更新,以说明它们将在 Python 3.12 中移除

    • configparser.SafeConfigParser

    • configparser.ParsingError.filename 属性

    • configparser.RawConfigParser.readfp() 方法

    (由 Hugo van Kemenade 在 bpo-45173 中贡献。)

  • configparser.LegacyInterpolation 自 Python 3.2 以来在文档字符串中已弃用,并且未在 configparser 文档中列出。它现在会发出一个 DeprecationWarning,并将在 Python 3.13 中移除。请改用 configparser.BasicInterpolationconfigparser.ExtendedInterpolation。(由 Hugo van Kemenade 在 bpo-46607 中贡献。)

  • 较旧的一组 importlib.resources 函数已被弃用,取而代之的是在 Python 3.9 中添加的替换函数,并且由于不支持位于包子目录中的资源,将在未来的 Python 版本中移除

    • importlib.resources.contents()

    • importlib.resources.is_resource()

    • importlib.resources.open_binary()

    • importlib.resources.open_text()

    • importlib.resources.read_binary()

    • importlib.resources.read_text()

    • importlib.resources.path()

  • locale.getdefaultlocale() 函数已弃用,将在 Python 3.15 中移除。请改用 locale.setlocale()locale.getpreferredencoding(False)locale.getlocale() 函数。(由 Victor Stinner 在 gh-90817 中贡献。)

  • locale.resetlocale() 函数已弃用,将在 Python 3.13 中移除。请改用 locale.setlocale(locale.LC_ALL, "")。(由 Victor Stinner 在 gh-90817 中贡献。)

  • 现在将对 正则表达式 中的数字组引用和组名称应用更严格的规则。现在,只有 ASCII 数字序列会被接受为数字引用,并且 bytes 模式和替换字符串中的组名称只能包含 ASCII 字母、数字和下划线。现在,对于违反这些规则的语法,会引发弃用警告。(由 Serhiy Storchaka 在 gh-91760 中贡献。)

  • re 模块中,re.template() 函数以及相应的 re.TEMPLATEre.T 标志已弃用,因为它们未文档化且缺乏明确的用途。它们将在 Python 3.13 中移除。(由 Serhiy Storchaka 和 Miro Hrončok 在 gh-92728 中贡献。)

  • turtle.settiltangle() 自 Python 3.1 以来已弃用;它现在会发出弃用警告,并将在 Python 3.13 中移除。请改用 turtle.tiltangle()(它之前被错误地标记为已弃用,并且它的文档字符串现在已更正)。(由 Hugo van Kemenade 在 bpo-45837 中贡献。)

  • typing.Text 仅用于在 Python 2 和 Python 3 代码之间提供兼容性支持,现在已弃用。目前尚未计划将其移除,但鼓励用户尽可能使用 str。(由 Alex Waygood 在 gh-92332 中贡献。)

  • 现在已弃用用于构造 typing.TypedDict 类型的关键字参数语法。支持将在 Python 3.13 中移除。(由 Jingchen Ye 在 gh-90224 中贡献。)

  • webbrowser.MacOSX 已弃用,将在 Python 3.13 中移除。它是未经测试的、未文档化的,并且 webbrowser 本身不使用它。(由 Donghee Na 在 bpo-42255 中贡献。)

  • 现在已弃用从 TestCaseIsolatedAsyncioTestCase 测试方法返回值(除了默认的 None 值)的行为。

  • 以下未正式文档化的 unittest 函数已被弃用,计划在 Python 3.13 中移除。

    • unittest.findTestCases()

    • unittest.makeSuite()

    • unittest.getTestCaseNames()

    请改用 TestLoader 方法。

    (由 Erlend E. Aasland 在 bpo-5846 中贡献。)

  • unittest.TestProgram.usageExit() 被标记为已弃用,将在 3.13 中移除。(由 Carlos Damázio 在 gh-67048 中贡献。)

计划在 Python 3.12 中移除

以下 Python API 在早期的 Python 版本中已被弃用,并将在 Python 3.12 中移除。

计划移除的 C API 将单独列出

  • asynchat 模块

  • asyncore 模块

  • 整个 distutils 包

  • imp 模块

  • typing.io 命名空间

  • typing.re 命名空间

  • cgi.log()

  • importlib.find_loader()

  • importlib.abc.Loader.module_repr()

  • importlib.abc.MetaPathFinder.find_module()

  • importlib.abc.PathEntryFinder.find_loader()

  • importlib.abc.PathEntryFinder.find_module()

  • importlib.machinery.BuiltinImporter.find_module()

  • importlib.machinery.BuiltinLoader.module_repr()

  • importlib.machinery.FileFinder.find_loader()

  • importlib.machinery.FileFinder.find_module()

  • importlib.machinery.FrozenImporter.find_module()

  • importlib.machinery.FrozenLoader.module_repr()

  • importlib.machinery.PathFinder.find_module()

  • importlib.machinery.WindowsRegistryFinder.find_module()

  • importlib.util.module_for_loader()

  • importlib.util.set_loader_wrapper()

  • importlib.util.set_package_wrapper()

  • pkgutil.ImpImporter

  • pkgutil.ImpLoader

  • pathlib.Path.link_to()

  • sqlite3.enable_shared_cache()

  • sqlite3.OptimizedUnicode()

  • PYTHONTHREADDEBUG 环境变量

  • 以下 unittest 中的已弃用别名

    已弃用别名

    方法名称

    弃用版本

    failUnless

    assertTrue()

    3.1

    failIf

    assertFalse()

    3.1

    failUnlessEqual

    assertEqual()

    3.1

    failIfEqual

    assertNotEqual()

    3.1

    failUnlessAlmostEqual

    assertAlmostEqual()

    3.1

    failIfAlmostEqual

    assertNotAlmostEqual()

    3.1

    failUnlessRaises

    assertRaises()

    3.1

    assert_

    assertTrue()

    3.2

    assertEquals

    assertEqual()

    3.2

    assertNotEquals

    assertNotEqual()

    3.2

    assertAlmostEquals

    assertAlmostEqual()

    3.2

    assertNotAlmostEquals

    assertNotAlmostEqual()

    3.2

    assertRegexpMatches

    assertRegex()

    3.2

    assertRaisesRegexp

    assertRaisesRegex()

    3.2

    assertNotRegexpMatches

    assertNotRegex()

    3.5

已移除

本节列出了在 Python 3.11 中已移除的 Python API。

已移除的 C API 将单独列出

  • 移除了 @asyncio.coroutine() 装饰器,该装饰器使基于旧式生成器的协程能够与 async / await 代码兼容。 该函数自 Python 3.8 起已弃用,并且最初计划在 Python 3.10 中移除。请改用 async def。(由 Illia Volochii 在 bpo-43216 中贡献。)

  • 移除了 asyncio.coroutines.CoroWrapper,该类用于在调试模式下包装基于旧式生成器的协程对象。(由 Illia Volochii 在 bpo-43216 中贡献。)

  • 由于存在重大的安全隐患,asyncio.loop.create_datagram_endpoint() 的 *reuse_address* 参数已在 Python 3.9 中禁用,现在已完全移除。 这是由于 UDP 中套接字选项 SO_REUSEADDR 的行为所致。(由 Hugo van Kemenade 在 bpo-45129 中贡献。)

  • 移除了 binhex 模块,该模块已在 Python 3.9 中弃用。 另外还移除了相关的,同样已弃用的 binascii 函数

    • binascii.a2b_hqx()

    • binascii.b2a_hqx()

    • binascii.rlecode_hqx()

    • binascii.rldecode_hqx()

    binascii.crc_hqx() 函数仍然可用。

    (由 Victor Stinner 在 bpo-45085 中贡献。)

  • 移除了在 Python 3.9 中已弃用的 distutils bdist_msi 命令。 请改用 bdist_wheel (wheel 包)。(由 Hugo van Kemenade 在 bpo-45124 中贡献。)

  • 移除了 __getitem__() 方法,这些方法来自 xml.dom.pulldom.DOMEventStreamwsgiref.util.FileWrapperfileinput.FileInput,它们自 Python 3.9 起已弃用。(由 Hugo van Kemenade 在 bpo-45132 中贡献。)

  • 移除了已弃用的 gettext 函数 lgettext()ldgettext()lngettext()ldngettext()。 还移除了 bind_textdomain_codeset() 函数、NullTranslations.output_charset()NullTranslations.set_output_charset() 方法,以及 translation()install() 的 *codeset* 参数,因为它们仅用于 l*gettext() 函数。(由 Donghee Na 和 Serhiy Storchaka 在 bpo-44235 中贡献。)

  • inspect 模块中移除:

    (由 Hugo van Kemenade 在 bpo-45320 中贡献。)

  • pathlib.PurePath 中移除了 __class_getitem__() 方法,因为它未使用并且在以前的版本中错误地添加了该方法。(由 Nikita Sobolev 在 bpo-46483 中贡献。)

  • 移除了 smtpd 模块中的 MailmanProxy 类,因为它在没有外部 mailman 包的情况下无法使用。(由 Donghee Na 在 bpo-35800 中贡献。)

  • 移除了已弃用的 _tkinter.TkappTypesplit() 方法。(由 Erlend E. Aasland 在 bpo-38371 中贡献。)

  • unittest 发现中移除了命名空间包支持。 它是在 Python 3.4 中引入的,但自 Python 3.7 起已被破坏。(由 Inada Naoki 在 bpo-23882 中贡献。)

  • 移除了未公开的私有方法 float.__set_format__(),它在 Python 3.7 中曾被称为 float.__setformat__()。其文档字符串表示:“你可能不希望使用这个函数。它主要用于 Python 的测试套件中。”(由 Victor Stinner 贡献,详见 bpo-46852。)

  • 移除了 --experimental-isolated-subinterpreters 配置标志(以及对应的 EXPERIMENTAL_ISOLATED_SUBINTERPRETERS 宏)。

  • Pynche — Pythonic 自然色彩和色调编辑器 — 已从 Tools/scripts 目录中移出,并独立于 Python 源代码树进行开发

移植到 Python 3.11

本节列出了之前描述的更改以及 Python API 中可能需要更改 Python 代码的其他错误修复。

C API 的移植说明单独列出

构建更改

  • CPython 现在对交叉编译到 WebAssembly 平台 Emscripten (wasm32-unknown-emscripten,即浏览器中的 Python) 和 WebAssembly 系统接口 (WASI) (wasm32-unknown-wasi) 具有 PEP 11 第 3 层支持。此项工作的灵感来自以前的工作,例如 Pyodide。这些平台提供 POSIX API 的有限子集;与网络、进程、线程、信号、mmap 和用户/组相关的 Python 标准库功能和模块不可用或无法工作。(Emscripten 由 Christian Heimes 和 Ethan Smith 贡献,详见 gh-84461,WASI 由 Christian Heimes 贡献,详见 gh-90473;平台在 gh-95085 中得到提升)

  • 现在构建 CPython 需要

  • 已移除 Py_NO_NAN 宏。由于 CPython 现在需要 IEEE 754 浮点数,因此始终可以使用 NaN 值。(由 Victor Stinner 贡献,详见 bpo-46656。)

  • tkinter 包现在需要 Tcl/Tk 版本 8.5.12 或更高版本。(由 Serhiy Storchaka 贡献,详见 bpo-46996。)

  • 现在由 configure 检测大多数 stdlib 扩展模块的构建依赖项、编译器标志和链接器标志。libffi、libnsl、libsqlite3、zlib、bzip2、liblzma、libcrypt、Tcl/Tk 和 uuid 标志由 pkg-config 检测(如果可用)。tkinter 现在需要 pkg-config 命令来检测 Tcl/Tk 标头和库的开发设置。(由 Christian Heimes 和 Erlend Egeberg Aasland 贡献,详见 bpo-45847bpo-45747bpo-45763。)

  • libpython 不再链接到 libcrypt。(由 Mike Gilbert 贡献,详见 bpo-45433。)

  • CPython 现在可以通过将 thin 传递给 --with-lto 来使用 ThinLTO 选项进行构建,即 --with-lto=thin。(由 Donghee Na 和 Brett Holman 贡献,详见 bpo-44340。)

  • 现在可以禁用对象结构的空闲列表。一个新的 configure 选项 --without-freelists 可以用来禁用除空元组单例之外的所有空闲列表。(由 Christian Heimes 在 bpo-45522 中贡献。)

  • Modules/SetupModules/makesetup 已经得到改进和整合。扩展模块现在可以通过 makesetup 构建。除了某些测试模块外,所有模块都可以静态链接到主二进制文件或库中。(由 Brett Cannon 和 Christian Heimes 在 bpo-45548bpo-45570bpo-45571bpo-43974 中贡献。)

    注意

    使用环境变量 TCLTK_CFLAGSTCLTK_LIBS 手动指定 Tcl/Tk 头文件和库的位置。 configure 选项 --with-tcltk-includes--with-tcltk-libs 已被删除。

    在 RHEL 7 和 CentOS 7 上,开发包不提供 tcl.pctk.pc;请使用 TCLTK_LIBS="-ltk8.5 -ltkstub8.5 -ltcl8.5"。目录 Misc/rhel7 包含 .pc 文件以及如何使用 RHEL 7 和 CentOS 7 的 Tcl/Tk 和 OpenSSL 构建 Python 的说明。

  • CPython 现在默认对 Python int 实现使用 30 位数字。之前,默认是在 SIZEOF_VOID_P >= 8 的平台上使用 30 位数字,否则使用 15 位数字。仍然可以通过配置脚本的 --enable-big-digits 选项或者(对于 Windows)PC/pyconfig.h 中的 PYLONG_BITS_IN_DIGIT 变量显式请求使用 15 位数字,但此选项可能会在未来的某个时候被删除。(由 Mark Dickinson 在 bpo-45569 中贡献。)

C API 更改

新功能

移植到 Python 3.11

  • 一些宏已转换为静态内联函数,以避免 宏陷阱。此更改对用户来说应该是基本透明的,因为替换函数会将其参数强制转换为预期类型,以避免由于静态类型检查导致的编译器警告。但是,当有限的 C API 设置为 >=3.11 时,这些强制转换不会执行,调用者需要将参数强制转换为其预期类型。有关更多详细信息,请参阅 PEP 670。(由 Victor Stinner 和 Erlend E. Aasland 在 gh-89653 中贡献。)

  • PyErr_SetExcInfo() 不再使用 typetraceback 参数,解释器现在从异常实例(value 参数)派生这些值。该函数仍然会窃取所有三个参数的引用。(由 Irit Katriel 在 bpo-45711 中贡献。)

  • PyErr_GetExcInfo() 现在从异常实例(value 字段)派生结果的 typetraceback 字段。(由 Irit Katriel 在 bpo-45711 中贡献。)

  • _frozen 有一个新的 is_package 字段,用于指示冻结模块是否为包。之前,size 字段中的负值是指标。现在 size 仅使用非负值。(由 Kumar Aditya 在 bpo-46608 中贡献。)

  • _PyFrameEvalFunction() 现在将 _PyInterpreterFrame* 作为其第二个参数,而不是 PyFrameObject*。有关如何使用此函数指针类型的更多详细信息,请参阅 PEP 523

  • PyCode_New()PyCode_NewWithPosOnlyArgs() 现在接受一个额外的 exception_table 参数。如果可能的话,应该避免使用这些函数。要获取自定义代码对象:使用编译器创建一个代码对象,然后使用 replace 方法获取修改后的版本。

  • PyCodeObject 不再具有 co_codeco_varnamesco_cellvarsco_freevars 字段。相反,请分别使用 PyCode_GetCode()PyCode_GetVarnames()PyCode_GetCellvars()PyCode_GetFreevars() 通过 C API 访问它们。(Brandt Bucher 在 bpo-46841 和 Ken Jin 在 gh-92154gh-94936 中贡献了此更改。)

  • 旧的垃圾回收宏 (Py_TRASHCAN_SAFE_BEGIN/Py_TRASHCAN_SAFE_END) 现在已弃用。它们应替换为新的宏 Py_TRASHCAN_BEGINPy_TRASHCAN_END

    一个具有旧宏的 tp_dealloc 函数,例如

    static void
    mytype_dealloc(mytype *p)
    {
        PyObject_GC_UnTrack(p);
        Py_TRASHCAN_SAFE_BEGIN(p);
        ...
        Py_TRASHCAN_SAFE_END
    }
    

    应迁移到新的宏,如下所示

    static void
    mytype_dealloc(mytype *p)
    {
        PyObject_GC_UnTrack(p);
        Py_TRASHCAN_BEGIN(p, mytype_dealloc)
        ...
        Py_TRASHCAN_END
    }
    

    请注意,Py_TRASHCAN_BEGIN 具有第二个参数,该参数应为它所在的释放函数。

    为了在同一个代码库中支持旧版本的 Python,您可以定义以下宏并在整个代码中使用它们(感谢:这些是从 mypy 代码库中复制的)

    #if PY_VERSION_HEX >= 0x03080000
    #  define CPy_TRASHCAN_BEGIN(op, dealloc) Py_TRASHCAN_BEGIN(op, dealloc)
    #  define CPy_TRASHCAN_END(op) Py_TRASHCAN_END
    #else
    #  define CPy_TRASHCAN_BEGIN(op, dealloc) Py_TRASHCAN_SAFE_BEGIN(op)
    #  define CPy_TRASHCAN_END(op) Py_TRASHCAN_SAFE_END(op)
    #endif
    
  • 如果类型定义了 Py_TPFLAGS_HAVE_GC 标志但没有 traverse 函数 (PyTypeObject.tp_traverse),则 PyType_Ready() 函数现在会引发错误。(Victor Stinner 在 bpo-44263 中贡献了此更改。)

  • 具有 Py_TPFLAGS_IMMUTABLETYPE 标志的堆类型现在可以继承 PEP 590 vectorcall 协议。以前,这仅适用于 静态类型。(Erlend E. Aasland 在 bpo-43908 中贡献了此更改)

  • 由于 Py_TYPE() 已更改为内联静态函数,因此 Py_TYPE(obj) = new_type 必须替换为 Py_SET_TYPE(obj, new_type):请参阅 Py_SET_TYPE() 函数(自 Python 3.9 起可用)。为了向后兼容,可以使用此宏

    #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)
    static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
    { ob->ob_type = type; }
    #define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type)
    #endif
    

    (Victor Stinner 在 bpo-39573 中贡献了此更改。)

  • 由于 Py_SIZE() 已更改为内联静态函数,因此 Py_SIZE(obj) = new_size 必须替换为 Py_SET_SIZE(obj, new_size):请参阅 Py_SET_SIZE() 函数(自 Python 3.9 起可用)。为了向后兼容,可以使用此宏

    #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE)
    static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size)
    { ob->ob_size = size; }
    #define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size)
    #endif
    

    (Victor Stinner 在 bpo-39573 中贡献了此更改。)

  • Py_LIMITED_API 宏设置为 0x030b0000 (Python 3.11) 或更高版本时,<Python.h> 不再包含头文件 <stdlib.h><stdio.h><errno.h><string.h>。C 扩展应在 #include <Python.h> 之后显式包含头文件。(Victor Stinner 在 bpo-45434 中贡献了此更改。)

  • 非限制 API 文件 cellobject.hclassobject.hcode.hcontext.hfuncobject.hgenobject.hlongintrepr.h 已移动到 Include/cpython 目录。此外,eval.h 头文件已被删除。这些文件不得直接包含,因为它们已包含在 Python.h 中:包含文件。如果它们已被直接包含,请考虑改为包含 Python.h。(Victor Stinner 在 bpo-35134 中贡献了此更改。)

  • PyUnicode_CHECK_INTERNED() 宏已从有限的 C API 中排除。它在那里永远不可用,因为它使用了有限的 C API 中不可用的内部结构。(Victor Stinner 在 bpo-46007 中贡献了此更改。)

  • 以下框架函数和类型现在可以通过 #include <Python.h> 直接使用,不再需要添加 #include <frameobject.h>

    (Victor Stinner 在 gh-93937 中贡献了此更改。)

  • PyFrameObject 结构的成员已从公共 C API 中删除。

    虽然文档指出 PyFrameObject 字段随时可能更改,但它们已经稳定了很长时间,并在几个流行的扩展中使用。

    在 Python 3.11 中,重新组织了帧结构以允许性能优化。一些字段被完全删除,因为它们是旧实现的细节。

    PyFrameObject 字段

    Python 帧对象现在是延迟创建的。一个副作用是,不能直接访问 f_back 成员,因为它的值现在也是延迟计算的。必须调用 PyFrame_GetBack() 函数来代替。

    直接访问 f_locals 的调试器必须调用 PyFrame_GetLocals() 来代替。他们不再需要调用 PyFrame_FastToLocalsWithError()PyFrame_LocalsToFast(),事实上,他们不应该调用这些函数。帧的必要更新现在由虚拟机管理。

    在 Python 3.8 及更早版本上定义 PyFrame_GetCode() 的代码

    #if PY_VERSION_HEX < 0x030900B1
    static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame)
    {
        Py_INCREF(frame->f_code);
        return frame->f_code;
    }
    #endif
    

    在 Python 3.8 及更早版本上定义 PyFrame_GetBack() 的代码

    #if PY_VERSION_HEX < 0x030900B1
    static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame)
    {
        Py_XINCREF(frame->f_back);
        return frame->f_back;
    }
    #endif
    

    或者使用 pythoncapi_compat 项目在旧版本的 Python 上获取这两个函数。

  • PyThreadState 结构成员的更改

    在 Python 3.8 及更早版本上定义 PyThreadState_GetFrame() 的代码

    #if PY_VERSION_HEX < 0x030900B1
    static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate)
    {
        Py_XINCREF(tstate->frame);
        return tstate->frame;
    }
    #endif
    

    在 Python 3.10 及更早版本上定义 PyThreadState_EnterTracing()PyThreadState_LeaveTracing() 的代码

    #if PY_VERSION_HEX < 0x030B00A2
    static inline void PyThreadState_EnterTracing(PyThreadState *tstate)
    {
        tstate->tracing++;
    #if PY_VERSION_HEX >= 0x030A00A1
        tstate->cframe->use_tracing = 0;
    #else
        tstate->use_tracing = 0;
    #endif
    }
    
    static inline void PyThreadState_LeaveTracing(PyThreadState *tstate)
    {
        int use_tracing = (tstate->c_tracefunc != NULL || tstate->c_profilefunc != NULL);
        tstate->tracing--;
    #if PY_VERSION_HEX >= 0x030A00A1
        tstate->cframe->use_tracing = use_tracing;
    #else
        tstate->use_tracing = use_tracing;
    #endif
    }
    #endif
    

    或者使用 pythoncapi-compat 项目 在旧版本的 Python 函数上获取这些函数。

  • 鼓励发行商使用优化的 Blake2 库 libb2 构建 Python。

  • 现在必须将 PyConfig.module_search_paths_set 字段设置为 1,以便初始化使用 PyConfig.module_search_paths 来初始化 sys.path。否则,初始化将重新计算路径并替换添加到 module_search_paths 的任何值。

  • PyConfig_Read() 不再计算初始搜索路径,并且不会在 PyConfig.module_search_paths 中填充任何值。要计算默认路径然后修改它们,请完成初始化并使用 PySys_GetObject() 来检索 sys.path 作为 Python 列表对象并直接修改它。

已弃用

  • 弃用以下用于配置 Python 初始化的函数

    • PySys_AddWarnOptionUnicode()

    • PySys_AddWarnOption()

    • PySys_AddXOption()

    • PySys_HasWarnOptions()

    • PySys_SetArgvEx()

    • PySys_SetArgv()

    • PySys_SetPath()

    • Py_SetPath()

    • Py_SetProgramName()

    • Py_SetPythonHome()

    • Py_SetStandardStreamEncoding()

    • _Py_SetProgramFullPath()

    使用 PyConfig API,即 Python 初始化配置 来代替 (PEP 587)。 (由 Victor Stinner 在 gh-88279 中贡献。)

  • 弃用 PyBytesObjectob_shash 成员。使用 PyObject_Hash() 代替。(由 Inada Naoki 在 bpo-46864 中贡献。)

在 Python 3.12 中待移除

以下 C API 在较早版本的 Python 中已弃用,并将在 Python 3.12 中移除。

  • PyUnicode_AS_DATA()

  • PyUnicode_AS_UNICODE()

  • PyUnicode_AsUnicodeAndSize()

  • PyUnicode_AsUnicode()

  • PyUnicode_FromUnicode()

  • PyUnicode_GET_DATA_SIZE()

  • PyUnicode_GET_SIZE()

  • PyUnicode_GetSize()

  • PyUnicode_IS_COMPACT()

  • PyUnicode_IS_READY()

  • PyUnicode_READY()

  • PyUnicode_WSTR_LENGTH()

  • _PyUnicode_AsUnicode()

  • PyUnicode_WCHAR_KIND

  • PyUnicodeObject

  • PyUnicode_InternImmortal()

已移除

  • PyFrame_BlockSetup()PyFrame_BlockPop() 已移除。(由 Mark Shannon 在 bpo-40222 中贡献。)

  • 移除以下使用 errno 变量的数学宏

    • Py_ADJUST_ERANGE1()

    • Py_ADJUST_ERANGE2()

    • Py_OVERFLOWED()

    • Py_SET_ERANGE_IF_OVERFLOW()

    • Py_SET_ERRNO_ON_MATH_ERROR()

    (由 Victor Stinner 在 bpo-45412 中贡献。)

  • 移除 Py_UNICODE_COPY()Py_UNICODE_FILL() 宏,这些宏自 Python 3.3 起已弃用。使用 PyUnicode_CopyCharacters()memcpy() (wchar_t* 字符串),以及 PyUnicode_Fill() 函数代替。(由 Victor Stinner 在 bpo-41123 中贡献。)

  • 移除 pystrhex.h 头文件。它只包含私有函数。C 扩展应仅包含主 <Python.h> 头文件。(由 Victor Stinner 在 bpo-45434 中贡献。)

  • 移除 Py_FORCE_DOUBLE() 宏。它被 Py_IS_INFINITY() 宏使用。(由 Victor Stinner 在 bpo-45440 中贡献。)

  • 定义 Py_LIMITED_API 时,以下项不再可用

    这些不属于 受限 API

    (由 Victor Stinner 在 bpo-45474 中贡献。)

  • 从受限 C API 中排除 PyWeakref_GET_OBJECT()。它从未起作用,因为在受限 C API 中 PyWeakReference 结构是不透明的。(由 Victor Stinner 在 bpo-35134 中贡献。)

  • 移除 PyHeapType_GET_MEMBERS() 宏。它被错误地暴露在公共 C API 中,它必须仅供 Python 内部使用。使用 PyTypeObject.tp_members 成员代替。(由 Victor Stinner 在 bpo-40170 中贡献。)

  • 移除 HAVE_PY_SET_53BIT_PRECISION 宏(已移至内部 C API)。(由 Victor Stinner 在 bpo-45412 中贡献。)

  • 移除 Py_UNICODE 编码器 API,因为它们自 Python 3.3 起已弃用,使用较少,并且相对于推荐的替代方案效率低下。

    删除的函数是

    • PyUnicode_Encode()

    • PyUnicode_EncodeASCII()

    • PyUnicode_EncodeLatin1()

    • PyUnicode_EncodeUTF7()

    • PyUnicode_EncodeUTF8()

    • PyUnicode_EncodeUTF16()

    • PyUnicode_EncodeUTF32()

    • PyUnicode_EncodeUnicodeEscape()

    • PyUnicode_EncodeRawUnicodeEscape()

    • PyUnicode_EncodeCharmap()

    • PyUnicode_TranslateCharmap()

    • PyUnicode_EncodeDecimal()

    • PyUnicode_TransformDecimalToASCII()

    有关详细信息,请参阅 PEP 624迁移指南。(由 Inada Naoki 在 bpo-44029 中贡献。)

3.11.4 中的显著变化

tarfile

  • tarfileshutil.unpack_archive() 中的提取方法新增了一个 filter 参数,用于限制可能令人意外或危险的 tar 功能,例如在目标目录之外创建文件。有关详细信息,请参阅 提取过滤器。在 Python 3.12 中,不使用 filter 参数将会显示 DeprecationWarning。在 Python 3.14 中,默认值将切换为 'data'。(由 Petr Viktorin 在 PEP 706 中贡献。)

3.11.5 中的显著变化

OpenSSL

  • 来自 python.org 的 Windows 构建版本和 macOS 安装程序现在使用 OpenSSL 3.0。