Python 3.12 有什么新变化

编者

TBD

本文介绍了 Python 3.12 相比 3.11 的新增特性。

更详细的信息可参阅 更新日志

备注

预发布版用户应当了解到此文档目前处于草稿状态。 它将随着 Python 3.12 的发布进行不断更新,因此即使已经阅读过较早的版本也仍然值得再次查看。

摘要 -- 发布重点

新的语法特性:

解释器的改进:

新的类型标注特性:

重要的弃用、移除或限制:

改进的错误消息

  • Modules from the standard library are now potentially suggested as part of the error messages displayed by the interpreter when a NameError is raised to the top level. (Contributed by Pablo Galindo in gh-98254.)

    >>> sys.version_info
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'sys' is not defined. Did you forget to import 'sys'?
    
  • Improve the error suggestion for NameError exceptions for instances. Now if a NameError is raised in a method and the instance has an attribute that's exactly equal to the name in the exception, the suggestion will include self.<NAME> instead of the closest match in the method scope. (Contributed by Pablo Galindo in gh-99139.)

    >>> class A:
    ...    def __init__(self):
    ...        self.blech = 1
    ...
    ...    def foo(self):
    ...        somethin = blech
    ...
    >>> A().foo()
      File "<stdin>", line 1
        somethin = blech
                   ^^^^^
    NameError: name 'blech' is not defined. Did you mean: 'self.blech'?
    
  • Improve the SyntaxError error message when the user types import x from y instead of from y import x. (Contributed by Pablo Galindo in gh-98931.)

    >>> import a.y.z from b.y.z
      File "<stdin>", line 1
        import a.y.z from b.y.z
        ^^^^^^^^^^^^^^^^^^^^^^^
    SyntaxError: Did you mean to use 'from ... import ...' instead?
    
  • ImportError exceptions raised from failed from <module> import <name> statements now include suggestions for the value of <name> based on the available names in <module>. (Contributed by Pablo Galindo in gh-91058.)

    >>> from collections import chainmap
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ImportError: cannot import name 'chainmap' from 'collections'. Did you mean: 'ChainMap'?
    

新的特性

PEP 701:f-字符串的句法形式化

PEP 701 lifts some restrictions on the usage of f-strings. Expression components inside f-strings can now be any valid Python expression, including strings reusing the same quote as the containing f-string, multi-line expressions, comments, backslashes, and unicode escape sequences. Let's cover these in detail:

  • Quote reuse: in Python 3.11, reusing the same quotes as the enclosing f-string raises a SyntaxError, forcing the user to either use other available quotes (like using double quotes or triple quotes if the f-string uses single quotes). In Python 3.12, you can now do things like this:

    >>> songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
    >>> f"This is the playlist: {", ".join(songs)}"
    'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
    

    请注意,在这一更改之前,对f-字符串的嵌套方式没有明确的限制,但字符串引号不能在f-字符串的表达式组件中重复使用,这使得不可能任意嵌套f-字符串。事实上,这是可以编写的嵌套最多的f-字符串:

    >>> f"""{f'''{f'{f"{1+1}"}'}'''}"""
    '2'
    

    由于现在f-字符串可以在表达式组件中包含任何有效的Python表达式,因此现在可以任意嵌套f-字符串:

    >>> f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"
    '2'
    
  • Multi-line expressions and comments: In Python 3.11, f-string expressions must be defined in a single line, even if the expression within the f-string could normally span multiple lines (like literal lists being defined over multiple lines), making them harder to read. In Python 3.12 you can now define f-strings spanning multiple lines, and add inline comments:

    >>> f"This is the playlist: {", ".join([
    ...     'Take me back to Eden',  # My, my, those eyes like fire
    ...     'Alkaline',              # Not acid nor alkaline
    ...     'Ascensionism'           # Take to the broken skies at last
    ... ])}"
    'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
    
  • Backslashes and unicode characters: before Python 3.12 f-string expressions couldn't contain any \ character. This also affected unicode escape sequences (such as \N{snowman}) as these contain the \N part that previously could not be part of expression components of f-strings. Now, you can define expressions like this:

    >>> print(f"This is the playlist: {"\n".join(songs)}")
    This is the playlist: Take me back to Eden
    Alkaline
    Ascensionism
    >>> print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")
    This is the playlist: Take me back to Eden♥Alkaline♥Ascensionism
    

更多细节请参见 PEP 701

As a positive side-effect of how this feature has been implemented (by parsing f-strings with the PEG parser, now error messages for f-strings are more precise and include the exact location of the error. For example, in Python 3.11, the following f-string raises a SyntaxError:

>>> my_string = f"{x z y}" + f"{1 + 1}"
  File "<stdin>", line 1
    (x z y)
     ^^^
SyntaxError: f-string: invalid syntax. Perhaps you forgot a comma?

但是错误消息不包括错误在行中的确切位置,而且表达式被人为地用括号括起来。在Python 3.12中,由于f-字符串是用PEG解析器解析的,因此错误消息可以更精确,并显示整行:

>>> my_string = f"{x z y}" + f"{1 + 1}"
  File "<stdin>", line 1
    my_string = f"{x z y}" + f"{1 + 1}"
                   ^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?

(由 Pablo Galindo、Batuhan Taskaya、 Lysandros Nikolaou、Cristián Maureira-Fredes 和 Marta Gómez 在 gh-102856 中贡献。 PEP 由 Pablo Galindo、 Batuhan Taskaya、 Lysandros Nikolaou 和 Marta Gómez 撰写)。

PEP 709:推导式内联

Dictionary, list, and set comprehensions are now inlined, rather than creating a new single-use function object for each execution of the comprehension. This speeds up execution of a comprehension by up to two times. See PEP 709 for further details.

Comprehension iteration variables remain isolated and don't overwrite a variable of the same name in the outer scope, nor are they visible after the comprehension. Inlining does result in a few visible behavior changes:

  • 回溯中的推导式不再有单独的帧,跟踪/评测也不再将推导式显示为函数调用。

  • symtable 模块将不再为每个推导式产生子符号表;取而代之的是,推导式的 locals 将包括在父函数的符号表中。

  • 在推导式内部调用 locals() 现在包括该推导式外部外部的变量,而不再包括推导式“参数”导致的 .0 合成变量。

  • 一个直接迭代 locals() 的推导式 (例如 [k for k in locals()]) 在启动追踪 (例如检测代码覆盖度) 的情况下运行时可能导致 "RuntimeError: dictionary changed size during iteration"。 此行为与现有的 for k in locals(): 等代码保持一致。 要避免此错误,可先创建一个由键组成的列表用于迭代: keys = list(locals()); [k for k in keys]

(Contributed by Carl Meyer and Vladimir Matveev in PEP 709.)

PEP 688: 使缓冲区协议在Python中可访问

PEP 688 引入了一种在 Python 代码中使用 缓冲区协议 的方法。实现 __buffer__() 方法的类现在可以作为缓冲区类型使用。

新的 collections.abc.Buffer ABC(抽象基类)提供了一种表示缓冲区对象的标准方法,例如在类型注释中。 新的 inspect.BufferFlags 枚举表示可用于自定义缓冲区创建的标志。 (由 Jelle Zijlstra 在 gh-102500 中贡献。)

PEP 684: 解释器级 GIL

PEP 684 introduces a per-interpreter GIL, so that sub-interpreters may now be created with a unique GIL per interpreter. This allows Python programs to take full advantage of multiple CPU cores. This is currently only available through the C-API, though a Python API is anticipated for 3.13.

使用新的 Py_NewInterpreterFromConfig() 函数来创建具有单独 GIL 的解释器:

PyInterpreterConfig config = {
    .check_multi_interp_extensions = 1,
    .gil = PyInterpreterConfig_OWN_GIL,
};
PyThreadState *tstate = NULL;
PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
if (PyStatus_Exception(status)) {
    return -1;
}
/* The new interpeter is now active in the current thread. */

有关如何将 C-API 用于子解释器和解释器级 GIL 的更多示例,请参见 Modules/_xxsubinterpretersmodule.c

(由 Eric Snow 在 gh-104210 等中贡献。)

PEP 669:针对 CPython 的低影响监控

PEP 669 defines a new API for profilers, debuggers, and other tools to monitor events in CPython. It covers a wide range of events, including calls, returns, lines, exceptions, jumps, and more. This means that you only pay for what you use, providing support for near-zero overhead debuggers and coverage tools. See sys.monitoring for details.

(Contributed by Mark Shannon in gh-103083.)

其他语言特性修改

  • Add support for the perf profiler through the new environment variable PYTHONPERFSUPPORT and command-line option -X perf, as well as the new sys.activate_stack_trampoline(), sys.deactivate_stack_trampoline(), and sys.is_stack_trampoline_active() functions. (Design by Pablo Galindo. Contributed by Pablo Galindo and Christian Heimes with contributions from Gregory P. Smith [Google] and Mark Shannon in gh-96123.)

  • The extraction methods in tarfile, and shutil.unpack_archive(), have a new a filter argument that allows limiting tar features than may be surprising or dangerous, such as creating files outside the destination directory. See tarfile extraction filters for details. In Python 3.14, the default will switch to 'data'. (Contributed by Petr Viktorin in PEP 706.)

  • 如果底层映射是可哈希的,那么 types.MappingProxyType 实例现在是可哈希的。 (由 Serhiy Storchaka 在 gh-87995 中贡献。)

  • memoryview 现在支持半浮点类型(“e”格式代码)。 (由 Dong-hee Na 和 Antoine Pitrou 在 gh-90751 中贡献。)

  • 解析器现在在解析包含空字节的源代码时引发 SyntaxError。 (由 Pablo Galindo 在 gh-96670 中贡献 。)

  • ast.parse() 现在会在解析包含空字节的源代码时引发 SyntaxError 而不是 ValueError。 (由 Pablo Galindo 在 gh-96670 中贡献 。)

  • 垃圾回收器现在只在 Python 字节码评估循环的 eval-breaker 机制上运行,而不是在对象分配上运行。 垃圾回收也可以在调用 PyErr_CheckSignals() 时运行,因此需要长时间运行而不执行任何 Python 代码的 C 扩展也有机会定期执行垃圾回收。 (由 Pablo Galindo 在 gh-97922 中贡献。)

  • A backslash-character pair that is not a valid escape sequence now generates a SyntaxWarning, instead of DeprecationWarning. For example, re.compile("\d+\.\d+") now emits a SyntaxWarning ("\d" is an invalid escape sequence, use raw strings for regular expression: re.compile(r"\d+\.\d+")). In a future Python version, SyntaxError will eventually be raised, instead of SyntaxWarning. (Contributed by Victor Stinner in gh-98401.)

  • 值大于 0o377 (例如: "\477") 的八进制转义序列,在 Python 3.11 中已弃用,现在会产生 SyntaxWarning,而不是 DeprecationWarning。 在未来的 Python 版本中,它们最终将是 SyntaxError。 (由 Victor Stinner 在 gh-98401 中贡献。)

  • 所有期望布尔参数的内置和扩展可调用函数现在都接受任何类型的参数,而不仅仅是 boolint。 (由 Serhiy Storchaka 在 gh-60203 中贡献。)

  • 未存储在推导式目标部分中的变量现在可以在赋值表达式 (:=) 中使用。 例如,在 [(b := 1) for a, b.prop in some_iter] 中,现在允许对 b 进行赋值。 请注意,根据 PEP 572,仍然不允许向存储在推导式目标部分中的变量 (如 a) 赋值。 (由 Nikita Sobolev 在 gh-100581 中贡献。)

  • slice 对象现在是可哈希的,允许它们用作字典的键和集合项。 (由 Will Bradshaw、Furkan Onder 和 Raymond Hettinger 在 gh-101264 中贡献。)

  • sum() 现在使用 Neumaier 求和来提高浮点求和或整数浮点混合求和的准确性。 (由 Raymond Hettinger 在 gh-100425 中贡献。)

  • Exceptions raised in a class or type's __set_name__ method are no longer wrapped by a RuntimeError. Context information is added to the exception as a PEP 678 note. (Contributed by Irit Katriel in gh-77757.)

  • try-except* 构造处理整个 ExceptionGroup 并引发另一个异常时,该异常不再封装在 ExceptionGroup 中。 在 3.11.4 版中也进行了更改。 (由 Irit Katriel 在 gh-103590 中贡献。)

新增模块

  • 无。

改进的模块

array

asyncio

calendar

csv

dis

  • Pseudo instruction opcodes (which are used by the compiler but do not appear in executable bytecode) are now exposed in the dis module. HAVE_ARGUMENT is still relevant to real opcodes, but it is not useful for pseudo instructions. Use the new dis.hasarg collection instead. (Contributed by Irit Katriel in gh-94216.)

fractions

importlib.resources

inspect

itertools

  • 增加了 itertools.batched() 用来收集到相同大小的元组其中最后一个批次的长度可能会比其他批次的短。 (由 Raymond Hettinger 在 gh-98363 中贡献。)

math

  • 添加了 math.sumprod() 用于计算乘积的和。 (由 Raymond Hettinger 在 gh-100485 中贡献。)

  • 扩展了 math.nextafter() 以包含一个 steps 参数,用于一次上移或下移多个步骤。 (由 Matthias Goergens、Mark Dickinson 和 Raymond Hettinger在 gh-94906 中贡献。)

os

  • 增加了 os.PIDFD_NONBLOCK 以在非阻塞模式下打开具有 os.pidfd_open() 的进程的文件描述符。 (由 Kumar Aditya 在 gh-93312 中贡献。)

  • os.DirEntry 现在包括一个 os.DirEntry.is_junction() 方法来检查该条目是否为目录联接。 (由 Charles Machalow 在 gh-99547 中贡献。)

  • 在 Windows 版中添加 os.listdrives()os.listvolumes()os.listmounts() 函数,用于枚举驱动器、卷和挂载点。 (由 Steve Dower 在 gh-102519 中贡献。)

  • os.stat()os.lstat() 现在在 Windows 系统上更准确了。 st_birthtime 字段现在将使用文件的创建时间,st_ctime 已弃用,但仍包含创建时间(但为了与其他平台保持一致,将来将返回最后一次元数据更改时间)。 st_dev 可以高达 64 位,st_ino 可以高达 128 位,具体取决于你的文件系统,并且 st_rdev 始终设置为零,而非不正确的值。 这两个函数在较新版本的 Windows 上将会明显更快。 (由 Steve Dower 在 gh-99726 中贡献。)

os.path

pathlib

pdb

  • 添加便利变量以临时保存调试会话的值,并提供对当前帧或返回值等值的快速访问。 (由高天在 gh-103693 中贡献。)

random

shutil

  • shutil.make_archive() 现在将 rootdir 参数传递给支持它的自定义存档程序。 在这种情况下,它不再临时将进程的当前工作目录更改为 rootdir 来执行存档。 (由 Serhiy Storchaka 在 gh-74696 中贡献。)

  • shutil.rmtree() 现在接受一个新的参数 onexc ,它是一个类似 onerror 的错误处理器,但它需要一个异常实例,而不是一个 (typ, val, tb) 三元组。 onerror 已弃用,并将在 Python 3.14 中移除。 (由 Irit Katriel 在 gh-102828 中贡献。)

  • shutil.which() 现在即使给定的 cmd 包含目录组件,在 Windows 系统上也会参考 PATHEXT 环境变量在 PATH 中查找匹配项。 (由 Charles Machalow 在 gh-103179 中贡献。)

    shutil.which() 将在 Windows 上查询可执行文件时调用 NeedCurrentDirectoryForExePathW,以确定是否应将当前工作目录预先设置为搜索路径。 (由 Charles Machalow 在 gh-103179 中贡献。)

    在 Windows 上 shutil.which() 将在搜索路径的其他地方直接匹配之前返回 cmd 与来自 PATHEXT 的组件相匹配的路径。 (由 Charles Machalow 在 gh-103179 中贡献。)

sqlite3

statistics

  • 扩展了 statistics.correlation()ranked 方法的形式来包括对分级数据的斯皮尔曼相关性计算。 (由 Raymond Hettinger 在 gh-95861 中贡献。)

sys

tempfile

threading

tkinter

  • 现在 tkinter.Canvas.coords() 会展平其参数。 它现在不仅接受单独参数形式的坐标 (x1, y1, x2, y2, ...) 以及由坐标组成的序列 ([x1, y1, x2, y2, ...]),也接受成对分组 ((x1, y1), (x2, y2), ...[(x1, y1), (x2, y2), ...]) 形式的坐标,就像 create_*() 方法一样。 (由 Serhiy Storchaka 在 gh-94473 中贡献。)

tokenize

types

typing

  • 针对 运行时可检测协议isinstance() 检测现在会使用 inspect.getattr_static() 而不是 hasattr() 来查找属性是否存在。 这意味着描述器和 __getattr__() 方法在针对运行时可检测协议的 isinstance() 检测期间不会被意外地求值。 但是,这也意味着某些原来被视为运行时可检测协议的实例的对象在 Python 3.12+ 上将不再被视为运行时可检测协议的实例,反之亦然。 大部分用户都不太可能受到这一改变的影响。 (由 Alex Waygood 在 gh-102433 中贡献。)

  • 现在运行时可检测协议的成员在运行时一旦创建了相应的类就将被视为“已冻结”。 作用于运行时可检测协议的猴子补丁属性将仍然可用,但不会再影响将对象与协议进行比较的 isinstance() 检测中。 例如:

    >>> from typing import Protocol, runtime_checkable
    >>> @runtime_checkable
    ... class HasX(Protocol):
    ...     x = 1
    ...
    >>> class Foo: ...
    ...
    >>> f = Foo()
    >>> isinstance(f, HasX)
    False
    >>> f.x = 1
    >>> isinstance(f, HasX)
    True
    >>> HasX.y = 2
    >>> isinstance(f, HasX)  # unchanged, even though HasX now also has a "y" attribute
    True
    

    应用这项改变是为了提高针对运行时可检测协议的 isinstance() 检测速度。

  • 针对 运行时可检测协议isinstance() 检测的性能表现有显著的改进。 对于具有少量成员的协议的 isinstance() 检测相比 3.11 应当至少有 2x 的提速,有些可能会有 20x 或更多的提速。 但是,对于具有十四个或更多成员的协议的 isinstance() 检测可能会慢于 Python 3.11。 (由 Alex Waygood 在 gh-74690gh-103193 中贡献。)

  • 现在所有 typing.TypedDicttyping.NamedTuple 类都具有 __orig_bases__ 属性。 (由 Adrian Garcia Badaracco 在 gh-103699 中贡献。)

  • typing.dataclass_transform() 添加了 frozen_default 形参。 (由 Erik De Bonte 在 gh-99957 中贡献。)

unicodedata

  • Unicode 数据库已更新到 15.0.0 版。 (由 Benjamin Peterson 在 gh-96734 中贡献。)

unittest

增加了 --durations 命令行选项,显示 N 个最慢的测试用例:

python3 -m unittest --durations=3 lib.tests.test_threading
.....
Slowest test durations
----------------------------------------------------------------------
1.210s     test_timeout (Lib.test.test_threading.BarrierTests)
1.003s     test_default_timeout (Lib.test.test_threading.BarrierTests)
0.518s     test_timeout (Lib.test.test_threading.EventTests)

(0.000 durations hidden.  Use -v to show these durations.)
----------------------------------------------------------------------
Ran 158 tests in 9.869s

OK (skipped=3)

(由 Giampaolo Rodola 在 bpo-4080 中贡献。)

uuid

性能优化

  • 从 Unicode 对象中移除了 wstrwstr_length 成员。 这使得对象大小在 64 位平台上减小了 8 个或 16 个字节。 (PEP 623) (由 Inada Naoki 在 gh-92536 中贡献。)

  • 增加了在构建进程中使用 BOLT 二进制优化器的试验性支持,这将使使性能提升 1-5%。 (由 Kevin Modzelewski 在 gh-90536 中贡献并由 Dong-hee Na 在 gh-101525 中加以改进。)

  • 对于包含分组引用的替换字符串的正则表达式替换(包括 re.sub()re.subn() 函数及对应的 re.Pattern 方法)可加速 2--3 倍。 (由 Serhiy Storchaka 在 gh-91524 中贡献。)

  • Speed up asyncio.Task creation by deferring expensive string formatting. (Contributed by Itamar Oren in gh-103793.)

  • 作为在 tokenize 模块中应用 PEP 701 所要求的更改的附带效果,tokenize.tokenize()tokenize.generate_tokens() 函数可加速至多 64%。 (由 Marta Gómez Macías 和 Pablo Galindo 在 gh-102856 中贡献。)

  • 通过新的 LOAD_SUPER_ATTR 指令加速 super() 方法调用和属性加载。 (由 Carl Meyer 和 Vladimir Matveev 在 gh-103497 中贡献。)

CPython 字节码的改变

演示和工具

  • 移除了包含旧演示脚本的 Tools/demo/ 目录。 其副本可在 old-demos project 中找到。 (由 Victor Stinner 在 gh-97681 中贡献。)

  • 移除了 Tools/scripts/ 目录下过时的示例脚本。 其副本可在 old-demos project 中找到。 (由 Victor Stinner 在 gh-97669 中贡献。)

弃用

计划在 Python 3.13 中移除

以下模块和 API 已在之前的 Python 发布版中弃用,并将在 Python 3.13 中移除。

模块 (参见 PEP 594):

其他模块:

  • lib2to3,以及 2to3 程序 (gh-84540)

API:

计划在 Python 3.14 中移除

计划在未来版本中移除

下列 API 在更早的 Python 版本中已被弃用并将被移除,但目前还没有确定它们的移除日期。

  • array'u' 格式代码 (gh-57281)

  • typing.Text (gh-92332)

  • 目前 Python 接受数字类字面值后面紧跟关键字的写法,例如 0in x, 1or x, 0if 1else 2。 它将允许像 [0x1for x in y] 这样令人困惑且模棱两可的表达式 (它可以被解读为 [0x1 for x in y] 或者 [0x1f or x in y])。 从本发布版开始,如果数字类字面值后面紧跟关键字 and, else, for, if, in, isor 中的一个将会引发弃用警告。 在未来的版本中它将改为语法警告,最终将改为语法错误。 (gh-87999)

移除

asynchat 和 asyncore

  • 这两个模块已根据 PEP 594 中的时间表被移除,它们从 Python 3.6 起已被弃用。 请改用 asyncio。 (由 Nikita Sobolev 在 gh-96580 中贡献。)

configparser

distutils

  • 移除了 distutils 包。 它已在 Python 3.10 中根据 PEP 632 "Deprecate distutils module" 被弃用。 对于仍然使用 distutils 且无法升级为使用其他工具的项目,可以安装 setuptools 项目:它仍然提供了 distutils。 (由 Victor Stinner 在 gh-92584 中贡献。)

ensurepip

  • ensurepip 中移除了捆绑的 setuptools wheel,并停止在由 venv 创建的环境中安装 setuptools。

    pip (>= 22.1) 不再要求在环境中安装 setuptools。 基于 setuptools (和基于 distutils) 的包仍然可通过 pip install 来使用,因为 pip 将在它用于构建包的构建环境中提供 setuptools

    在默认情况下由 venv 创建或通过 ensurepip 初始化的环境将不再提供 easy_install, pkg_resources, setuptoolsdistutils 包,因为它们是 setuptools 包的组成部分。 对于在运行时依赖这些包的项目,应当将 setuptools 项目声明为依赖项之一并单独安装(通常是使用 pip)。

    (由 Pradyun Gedam 在 gh-95299 中贡献。)

enum

  • 移除了 enumEnumMeta.__getattr__,枚举属性访问已不再需要它。 (由 Ethan Furman 在 gh-95083 中贡献。)

ftplib

  • 移除了 ftplibFTP_TLS.ssl_version 类属性:请改用 context 形参。 (由 Victor Stinner 在 gh-94172 中贡献。)

gzip

  • 移除了 gzipgzip.GzipFilefilename 属性,自 Python 2.6 起该属性已被弃用,请改用 name 属性。 在可写模式下,如果 filename 属性没有 '.gz' 文件扩展名则会添加它。 (由 Victor Stinner 在 gh-94196 中贡献。)

hashlib

  • 移除了 hashlibhashlib.pbkdf2_hmac() 的纯 Python 实现,它在 Python 3.10 中已被弃用。 Python 3.10 及更新版本需要 OpenSSL 1.1.1 (PEP 644):该 OpenSSL 版本提供了 pbkdf2_hmac() 的更快速的 C 实现。 (由 Victor Stinner 在 gh-94199 中贡献。)

importlib

  • importlib 中许多先前已弃用对象的清理工作现已完成:

    • module_repr() 的引用和支持已被移除。 (由 Barry Warsaw 在 gh-97850 中贡献。)

    • importlib.util.set_package, importlib.util.set_loaderimportlib.util.module_for_loader 均已被移除。 (由 Brett Cannon 和 Nikita Sobolev 在 gh-65961gh-97850 中贡献。)

    • find_loader()find_module() API 的支持已被移除。 (由 Barry Warsaw 在 gh-98040 中贡献。)

    • importlib.abc.Finder, pkgutil.ImpImporterpkgutil.ImpLoader 已被移除。 (由 Barry Warsaw 在 gh-98040 中贡献。)

imp

  • imp 模块已被移除。 (由 Barry Warsaw 在 gh-98040 中贡献。)

  • 将已移除的 imp 函数替换为 importlib 函数:

    imp

    importlib

    imp.NullImporter

    None 插入到 sys.path_importer_cache

    imp.cache_from_source()

    importlib.util.cache_from_source()

    imp.find_module()

    importlib.util.find_spec()

    imp.get_magic()

    importlib.util.MAGIC_NUMBER

    imp.get_suffixes()

    importlib.machinery.SOURCE_SUFFIXES, importlib.machinery.EXTENSION_SUFFIXESimportlib.machinery.BYTECODE_SUFFIXES

    imp.get_tag()

    sys.implementation.cache_tag

    imp.load_module()

    importlib.import_module()

    imp.new_module(name)

    types.ModuleType(name)

    imp.reload()

    importlib.reload()

    imp.source_from_cache()

    importlib.util.source_from_cache()

  • imp.load_source() 替换为:

    import importlib.util
    import importlib.machinery
    
    def load_source(modname, filename):
        loader = importlib.machinery.SourceFileLoader(modname, filename)
        spec = importlib.util.spec_from_file_location(modname, filename, loader=loader)
        module = importlib.util.module_from_spec(spec)
        # The module is always executed and not cached in sys.modules.
        # Uncomment the following line to cache the module.
        # sys.modules[module.__name__] = module
        loader.exec_module(module)
        return module
    
  • 已移除 imp 函数和属性并且没有替选项:

    • 未写入文档的函数:

      • imp.init_builtin()

      • imp.load_compiled()

      • imp.load_dynamic()

      • imp.load_package()

    • imp.lock_held(),``imp.acquire_lock()``,``imp.release_lock()``: 加锁方案在 Python 3.3 中已改为模块级锁。

    • imp.find_module() 常量: SEARCH_ERROR, PY_SOURCE, PY_COMPILED, C_EXTENSION, PY_RESOURCE, PKG_DIRECTORY, C_BUILTIN, PY_FROZEN, PY_CODERESOURCE, IMP_HOOK

io

  • 移除了 io 中的 io.OpenWrapper_pyio.OpenWrapper,它们在 Python 3.10 中已被弃用:请改用 open()open() (io.open()) 函数是一个内置函数。 自 Python 3.10 起,_pyio.open() 也是一个静态方法。 (由 Victor Stinner 在 gh-94169 中贡献。).)

locale

  • 移除了 localelocale.format() 函数,它在 Python 3.7 中已被弃用:请改用 locale.format_string()。 (由 Victor Stinner 在 gh-94226 中贡献。)

  • smtpd: 该模块已按照 PEP 594 中的计划表被移除,它在 Python 3.4.7 和 3.5.4 中已被弃用。 请改用 aiosmtpd PyPI 模块或任何其他基于 asyncio Oleg Iarygin 在 gh-93243 中贡献。)

sqlite3

  • 以下未写入文档的 sqlite3 特性,在 Python 3.10 中已被弃用,现在已被移除:

    • sqlite3.enable_shared_cache()

    • sqlite3.OptimizedUnicode

    如果必须使用共享缓存,请在以 URI 模式打开数据库时使用 cache=shared 查询参数。

    sqlite3.OptimizedUnicode 文本工厂函数自 Python 3.3 起已成为 str 的一个别名。 之前将文本工厂设为 OptimizedUnicode 的代码可以显式地使用 str,或者依赖同样为 str 的默认值。

    (由 Erlend E. Aasland 在 gh-92548 中贡献。)

ssl

  • 移除了 sslssl.RAND_pseudo_bytes() 函数,它在 Python 3.6 中已被弃用:请改用 os.urandom()ssl.RAND_bytes()。 (由 Victor Stinner 在 gh-94199 中贡献。)

  • 移除了 ssl.match_hostname() 函数。 它在 Python 3.7 中已被弃用。 OpenSSL 自 Python 3.7 起将会执行主机名匹配,Python 已不再使用 ssl.match_hostname() 函数。 (由 Victor Stinner 在 gh-94199 中贡献。)

  • 移除了 ssl.wrap_socket() 函数,它在 Python 3.7 中已被弃用:请改为创建一个 ssl.SSLContext 对象并调用其 ssl.SSLContext.wrap_socket 方法。 任何仍然使用 ssl.wrap_socket() 的包都不再适用并且是不安全的。 该函数既不会发送 SNI TLS 扩展也不会验证服务器主机名。 其代码会受到 CWE-295: 不正确的证书验证问题的影响。 (由 Victor Stinner 在 gh-94199 中贡献。)

unittest

webbrowser

  • webbrowser 移除了对过时浏览器的支持。 被移除的浏览器包括: Grail, Mosaic, Netscape, Galeon, Skipstone, Iceape, Firebird 和 Firefox 35 及以下的版本 (gh-102871)。

xml.etree.ElementTree

  • 移除了纯 Python 实现的 ElementTree.Element.copy() 方法,该方法在 Python 3.10 中已被弃用,请改用 copy.copy() 函数。 xml.etree.ElementTree 的 C 实现没有 copy() 方法,只有 __copy__() 方法。 (由 Victor Stinner 在 gh-94383 中贡献。)

zipimport

  • 移除了 zipimportfind_loader()find_module() 方法,它们在 Python 3.10 中已被弃用:请改用 find_spec() 方法。 请参阅 PEP 451 了解相关说明。 (由 Victor Stinner 在 gh-94379 中贡献。)

其他事项

  • 从文档 Makefile 中移除了 suspicious 规则,并移除了 Doc/tools/rstlint.py,请改用 sphinx-lint。 (由 Julien Palard 在 gh-98179 中贡献。)

  • 移除了 ftplibimaplibpoplibsmtplib 模块中的 keyfilecertfile 形参数,以及 http.client 模块中的 key_filecert_filecheck_hostname 形参,它们自 Python 3.6 起都已被弃用。 请改用 context 形参(在 imaplib 中为 ssl_context 形参)。 (由 Victor Stinner 在 gh-94172 中贡献。).)

移植到 Python 3.12

本节列出了先前描述的更改以及可能需要更改代码的其他错误修正.

Python API 的变化

  • 现在对于正则表达式中的数字分组引用和分组名称将应用更严格的规则。 现在只接受 ASCII 数字序列作为数字引用。 字节串模式和替换字符串中的分组名称现在只能包含 ASCII 字母、数字和下划线。 (由 Serhiy Storchaka 在 gh-91760 中贡献。)

  • 移除了自 Python 3.10 起已被弃用的 randrange() 功能。 以前,randrange(10.0) 会无损地转换为 randrange(10)。 现在,它将引发 TypeError。 此外,对于非整数值如 randrange(10.5)randrange('10') 所引发的异常已从 ValueError 改为 TypeError。 这也防止了 randrange(1e25) 会从比 randrange(10**25) 更大的范围中静默选择的问题。 (最初由 Serhiy Storchaka 在 gh-86388 中提议。)

  • argparse.ArgumentParser 将从文件(例如 fromfile_prefix_chars 选项)读取参数的编码格式和错误处理句柄从默认的文本编码格式(例如 locale.getpreferredencoding(False) 调用)改为 filesystem encoding and error handler。 在 Windows 系统中参数文件应使用 UTF-8 而不是 ANSI 代码页来编码。

  • 删除了在 Python 3.4.7 和 3.5.4 中已被弃用的 asyncore-based smtpd 模块。 推荐使用基于 asyncioaiosmtpd PyPI 模块来替代。

  • shlex.split(): 传入 None 作为 s 参数现在将引发异常,而不是读取 sys.stdin。 该特性在 Python 3.9 中已被弃用。 (由 Victor Stinner 在 gh-94352 中贡献。)

  • os 模块不再接受类似字节串的路径,如 bytearraymemoryview 类型:只接受明确的 bytes 类型字节串。 (由 Victor Stinner 在 gh-98393 中贡献。)

  • 现在 syslog.openlog()syslog.closelog() 如果在子解释器中使用将失败。 syslog.syslog() 仍可在子解释器中使用,但前提是 syslog.openlog() 已在主解释器中被调用。 这些新限制不适用于主解释器,因此只有少数用户可能会受到影响。 这一改变有助于实现解释器隔离。 此外, syslog 是一个针对进程全局资源的包装器,而这些资源最好是通过主解释器来管理。 (由 Dong-hee Na 在 gh-99127 中贡献。)

  • 未写入文档的 cached_property() 的锁定行为已被移除,因为该行为会在类的所有实例中锁定,从而导致高锁定争用。 这意味着如果两个线程同时运行,缓存属性获取函数现在可以在单个实例中运行不止一次。 对于大多数简单的缓存属性(例如那些幂等的并且只需根据实例的其他属性计算一个值的属性)来说这是没有问题的。 如果需要同步,可在缓存属性获取函数中或多线程访问点周围实现锁定操作。

  • 现在 sys._current_exceptions() 将返回从线程 ID 到异常实例的映射,而不是到 (typ, exc, tb) 元组的映射。 (由 Irit Katriel 在 gh-103176 中贡献。)

  • 当使用 tarfileshutil.unpack_archive() 提取 tar 文件时,请传入 filter 参数来限制可能令人感到意外或危险的特性。 请参阅 解压缩过滤器 了解详情。

  • 由于在 PEP 701 中引入的更改 tokenize.tokenize()tokenize.generate_tokens() 函数的输出现在发生了改变。 这意味着不再为 f-字符输出 STRING 词元而是改为产生 PEP 701 中描述的词元:除了用于对表达式组件进行分词的适当词元外现在还有 FSTRING_START, FSTRING_MIDDLEFSTRING_END 会被用于 f-字符串的“字符串”部分。 例如对于 f-字符串 f"start {1+1} end" 旧版本的分词器会生成:

    1,0-1,18:           STRING         'f"start {1+1} end"'
    

    而新版本将生成:

    1,0-1,2:            FSTRING_START  'f"'
    1,2-1,8:            FSTRING_MIDDLE 'start '
    1,8-1,9:            OP             '{'
    1,9-1,10:           NUMBER         '1'
    1,10-1,11:          OP             '+'
    1,11-1,12:          NUMBER         '1'
    1,12-1,13:          OP             '}'
    1,13-1,17:          FSTRING_MIDDLE ' end'
    1,17-1,18:          FSTRING_END    '"'
    

    此外,支持 PEP 701 所需的改变还可能会导致一些细微的行为改变。 这些变化包括:

    • 在对一些无效 Python 字符如 ! 进行分词时相应词元的 type 属性已从 ERRORTOKEN 变为 OP

    • 不完整的单行字符串现在也会像不完整的多行字符串一样引发 tokenize.TokenError

    • 某些不完整或无效的 Python 代码现在会引发 tokenize.TokenError 而不是在执行分词时返回任意的 ERRORTOKEN 词元。

    • 在同一文件中混合使用制表符和空格作为缩进不再受到支持而是会引发 TabError

构建变化

  • Python 不再使用 setup.py 来构建共享的 C 扩展模块。 头文件和库等编译参数会在 configure 脚本中检测。 扩展将由 Makefile 来构建。 大多数扩展使用 pkg-config 并回退为手动检测。 (由 Christian Heimes 在 gh-93939 中贡献。)

  • 现在需要用带有两个形参的 va_start(),如 va_start(args, format), 来构建 Python。 现在将不会再调用单个形参的 va_start()。 (由 Kumar Aditya 在 gh-93207 中贡献。)

  • 现在如果 Clang 编译器接受 ThinLTO 选项则 CPython 会将其作为默认的链接时间优化策略。 (由 Dong-hee Na 在 gh-89536 中贡献。)

  • 在 Makefile 中添加了 COMPILEALL_OPTS 变量以覆盖 make install 中的 compileall 选项 (默认值: -j0)。 并将 3 条 compileall 命令合并为单条命令以便一次性构建所有优化级别 (0, 1, 2) 的 .pyc 文件。 (由 Victor Stinner 在 gh-99289 中贡献。)

  • 为 64 位 LoongArch 添加了平台三选项:

    • loongarch64-linux-gnusf

    • loongarch64-linux-gnuf32

    • loongarch64-linux-gnu

    (由 Zhang Na 在 gh-90656 中贡献。).)

  • PYTHON_FOR_REGEN 现在需要 Python 3.10 或更新版本。

  • 现在需要有 autoconf 2.71 和 aclocal 1.16.4 才能重新生成 !configure。 (由 Christian Heimes 在 gh-89886 中贡献。)

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

C API 的变化

新的特性

移植到 Python 3.12

  • 基于 Py_UNICODE* 表示形式的旧式 Unicode API 已被移除。 请迁移到基于 UTF-8 或 wchar_t* 的 API。

  • PyArg_ParseTuple() 等参数解析函数不再支持基于 Py_UNICODE* 的格式(例如 u, Z 等)。 请迁移到其他 Unicode 格式如 s, z, esU

  • tp_weaklist 对于所有静态内置类型将始终为 NULL。 这是 PyTypeObject 上的一个内部专属字段,但我们还是要指出这一变化以防有人碰巧仍然直接访问到该字段。 为避免出现中断,请考虑改用现有的公共 C-API,或在必要时使用(仅限内部使用的)宏 _PyObject_GET_WEAKREFS_LISTPTR()

  • 现在这个内部专用的 PyTypeObject.tp_subclasses 可能不是一个有效的对象指针。 为了反映这一点我们将其类型改为 void*。 我们提到这一点是为了防止有人碰巧直接访问到这个内部专用字段。

    要获取子类的列表,请调用 Python 方法 __subclasses__() (例如使用 PyObject_CallMethod())。

  • PyUnicode_FromFormat()PyUnicode_FromFormatV() 中添加对更多格式选项(左对齐、八进制、大写十六进制、intmax_tptrdiff_twchar_t C 字符串、可变宽度和精度)的支持。 (由 Serhiy Storchaka 在 gh-98836 中贡献。)

  • PyUnicode_FromFormat()PyUnicode_FromFormatV() 中未被识别的格式字符现在会设置一个 SystemError。 在之前的版本中它会导致格式字符串的所有其他部分被原样复制到结果字符串中,并丢弃任何额外的参数。 (由 Serhiy Storchaka 在 gh-95781 中贡献。)

  • 修复了 PyUnicode_FromFormat()PyUnicode_FromFormatV() 中错误的标志位置。 (由 Philip Georgi 在 gh-95504 中贡献。)

  • 希望添加 __dict__ 或弱引用槽位的扩展类应分别使用 Py_TPFLAGS_MANAGED_DICTPy_TPFLAGS_MANAGED_WEAKREF 来代替 tp_dictoffsettp_weaklistoffset。 目前仍支持使用 tp_dictoffsettp_weaklistoffset,但并不完全支持多重继承 (gh-95589),而且性能可能会变差。 声明了 Py_TPFLAGS_MANAGED_DICT 的类应当调用 _PyObject_VisitManagedDict()_PyObject_ClearManagedDict() 来遍历并清除它们的实例的字典。 要清除弱引用,请像之前一样调用 PyObject_ClearWeakRefs()

  • PyUnicode_FSDecoder() 函数不再接受类似字节串的路径,如 bytearraymemoryview 类型:只接受明确的 bytes 类型字节字符串。 (由 Victor Stinner 在 gh-98393 中贡献。)

  • Py_CLEARPy_SETREFPy_XSETREF 宏现在只会对其参数求值一次。如果参数有附带影响,这些附带影响将不会再重复。 (由 Victor Stinner 在 gh-98724 中贡献。)

  • 解释器的错误指示器现在总是规范化的。 这意味着 PyErr_SetObject()PyErr_SetString() 以及其他设置错误指示器的函数在保存异常之前都会将其规范化。 (由 Mark Shannon 在 gh-101578 中贡献。)

  • _Py_RefTotal 已不再具有重要性而保留它只是为了 ABI 的兼容性。 请注意,这是一个内部全局变量并且仅在调试版本中可用。 如果你碰巧要使用它那么你需要开始使用 _Py_GetGlobalRefTotal()

  • 下面的函数将为新创建的类型选择一个合适的元类:

    创建具有重载了 tp_new 的元类的类的做法已被弃用,在 Python 3.14+ 中将被禁止。 请注意这些函数会忽略元类的 tp_new,从而可能导致不完整的初始化。

    请注意 PyType_FromMetaclass() (在 Python 3.12 中新增) 已禁止创建具有重载了 tp_new (在 Python 中为 __new__() ) 的元类的类。

    由于 tp_new 重载了``PyType_From*`` 函数的几乎所有内容,因此两者互不兼容。 现有的行为 -- 在创建类型的一些步骤中忽略元类 -- 通常都是不安全的,因为(元)类会假定 tp_new 已被调用。 目前还没有简单通用的绕过方式。 以下办法之一可能对你有用:

    • 如果你控制着元类,请避免在其中使用 tp_new:

      • 如初始化可被跳过,则可以改在 tp_init 中完成。

      • 如果元类不需要从 Python 执行实例化,则使用 Py_TPFLAGS_DISALLOW_INSTANTIATION 旗标将其 tp_new 设为 NULL。 这将使其可被 PyType_From* 函数接受。

    • 避免使用 PyType_From* 函数:如果不需要 C 专属的特性(槽位或设置实例大小),请通过 调用 元类来创建类型。

    • 如果你 知道 可以安全地跳过 tp_new,就使用 Python 中的 warnings.catch_warnings() 过滤掉弃用警告。

  • PyOS_InputHookPyOS_ReadlineFunctionPointer 将不再在 子解释器 中被调用。 这是因为客户端通常依赖进程级的全局状态(而这些回调没有办法恢复扩展模块状态)。

    这也避免了扩展程序在不支持(或尚未被加载)的子解释器中运行的情况。 请参阅 gh-104668 了解更多信息。

  • PyLongObject 对其内部字段进行了修改以提高性能。 虽然 PyLongObject 的内部字段是私有的,但某些扩展模块会使用它们。 内部字段不应再被直接访问,而应改用以 PyLong_... 打头的 API 函数。 新增了两个 暂定 API 函数用于高效访问适配至单个机器字的 PyLongObject 的值:

  • 通过 PyMem_SetAllocator() 设置的自定义分配器现在必须是线程安全的,无论内存域是什么。 没有自己的状态的分配器,包括“钩子”将不会受影响。 如果你的自定义分配器还不是线程安全的且你需要指导则请创建一个新的 GitHub 问题并抄送给 @ericsnowcurrently

弃用

移除

  • 移除了 token.h 头文件。 从来就没有任何公用的分词器 C API。 token.h 头文件只是为 Python 内部使用而设计的。 (由 Victor Stinner 在 gh-92651 中贡献。)

  • 旧式 Unicode API 已被移除。 请参阅 PEP 623 了解详情。for detail.

    • PyUnicode_WCHAR_KIND

    • PyUnicode_AS_UNICODE()

    • PyUnicode_AsUnicode()

    • PyUnicode_AsUnicodeAndSize()

    • PyUnicode_AS_DATA()

    • PyUnicode_FromUnicode()

    • PyUnicode_GET_SIZE()

    • PyUnicode_GetSize()

    • PyUnicode_GET_DATA_SIZE()

  • 移除了 PyUnicode_InternImmortal() 函数宏。 (由 Victor Stinner 在 gh-85858 中贡献。).)

  • 从多个标准库模块和测试中移除了 Jython 兼容性处理。 (由 Nikita Sobolev 在 gh-99482 中贡献。)

  • ctypes 模块移除了 _use_broken_old_ctypes_structure_semantics_ 旗标。 (由 Nikita Sobolev 在 gh-99285 中贡献。)