Python 3.13 有什么新变化

  • 编者:
  • Adam Turner 和 Thomas Wouters

本文介绍了 Python 3.13 相比 3.12 增加的新特性。 Python 3.13 已于 2024 年 10 月 7 日发布。 要获取详细信息,可参阅 更新日志

参见

PEP 719 [https://peps.python.org/pep-0719/] — Python 3.13 发布计划

摘要 — 发布重点

Python 3.13 是 Python 编程语言的最新稳定发布版,包含多项针对语言、实现和标准库的改变。 最大的变化包括一个新的 交互式解释器,以及对于在 自由线程模式 ( PEP 703 [https://peps.python.org/pep-0703/]) 下运行和 即时编译器 ( PEP 744 [https://peps.python.org/pep-0744/]) 的实验性支持。

错误消息继续得到改进,回溯信息现在默认使用彩色高亮显示。 locals() 内置函数现在对于修改所返回的映射具有 更细化的语法,并且类型形参现在支持设置默认值。

针对标准库的改变包括移除已弃用的 API 和模块,以及用户友好度和正确性方面的常规提升。 一些旧式标准库模块自 Python 3.11 起被弃用 ( PEP 594 [https://peps.python.org/pep-0594/]) 之后现在 已被移除

本文并不试图提供所有新特性的完整规范说明,而是提供一个方便的概览。 要了解完整细节请参阅相应文档,如 标准库参数语言参考。 要了解某项改变的完整实现和设计理念,请参阅相应新特性的 PEP;但请注意一旦某项特性已完全实现则相应 PEP 通常不会再继续更新。 请参阅 迁移到 Python 3.13 了解如何从较早 Python 进行升级的指导。


解释器的改进:

对 Python 数据模型的改进:

标准库中的重大改进:

安全改进:

C API 的改进:

新的类型标注特性:

平台支持:

重要的移除:

  • PEP 594: 剩余的 19 个“死电池”(老旧 stdlib 模块)已从标准库中移除: aifc, audioop, cgi, cgitb, chunk, crypt, imghdr, mailcap, msilib, nis, nntplib, ossaudiodev, pipes, sndhdr, spwd, sunau, telnetlib, uuxdrlib

  • 移除了 2to3 工具和 lib2to3 模块(在 Python 3.11 中已被弃用)。

  • 移除了 tkinter.tix 模块(在 Python 3.6 中已被弃用)。

  • 移除了 locale.resetlocale() 函数。

  • 移除了 typing.iotyping.re 命名空间。

  • 移除了链式的 classmethod 描述器。

发布计划的变化:

PEP 602 [https://peps.python.org/pep-0602/] ("Annual Release Cycle for Python") 已被更新为将新发布版的完整支持 ('bugfix') 期扩展至两年。 这个更新的政策意味着:

  • Python 3.9—3.12 有一年半的完整支持,另加三年半的安全修正。

  • Python 3.13 及以后的版本有两年的完整支持,另加三年的安全修正。

新的特性

更好的交互式解释器

Python 现在默认会使用新的 interactive shell,它基于来自 PyPy 项目 [https://pypy.org/] 的代码。 当使用从交互式终端启动 REPL 时,下列新特性将受到支持:

  • 多行编辑并保留历史记录。

  • 对 REPL 专属的命令如 help, exit 和 quit 的直接支持,无需以函数形式调用它们。

  • 提示和回溯 默认启用彩色显示

  • 使用 F1 浏览交互式帮助并带有单独的命令历史。

  • 使用 F2 浏览去除了输出以及 >>> 提示符的历史。

  • 使用 F3 进入“粘贴模式”以更方便地粘贴大段代码(再次按 F3 返回常规提示符)。

要禁用新的交互式 shell,可设置 PYTHON_BASIC_REPL 环境变量。 有关交互模式的详情,请参见 交互模式

(由 Pablo Galindo Salgado, Łukasz Langa 和 Lysandros Nikolaou 在 gh-111201 [https://github.com/python/cpython/issues/111201] 基于来自 PyPy 项目的代码贡献。 Windows 支持由 Dino Viehland 和 Anthony Shaw 贡献。)

改进的错误消息

  • 一个常见错误是撰写的脚本和标准库中的某个模块重名。现在出现此类错误时会显示一条更有用的错误信息:
  1. $ python random.py
  2. Traceback (most recent call last):
  3. File "homeme/random.py", line 1, in <module> import random
  4. File "homeme/random.py", line 3, in <module> print(random.randint(5)) ^^^^^^^^^^^^^^
  5. AttributeError: module 'random' has no attribute 'randint' (consider renaming 'homeme/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)

类似地,如果一个脚本具有与它尝试导入的第三方模块相同的名称并因此导致错误,我们也会显示一条更有帮助的错误消息:

  1. $ python numpy.py
  2. Traceback (most recent call last):
  3. File "homeme/numpy.py", line 1, in <module> import numpy as np
  4. File "homeme/numpy.py", line 3, in <module> np.array([1, 2, 3]) ^^^^^^^^
  5. AttributeError: module 'numpy' has no attribute 'array' (consider renaming 'homeme/numpy.py' if it has the same name as a library you intended to import)

(由 Shantanu Jain 在 gh-95754 [https://github.com/python/cpython/issues/95754] 中贡献)。

  • 现在当向一个函数传入不正确的关键字参数时错误消息会尝试提示正确的关键字参数。
  1. >>> "Better error messages!".split(max_split=1)
  2. Traceback (most recent call last):
  3. File "<python-input-0>", line 1, in <module> "Better error messages!".split(max_split=1) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  4. TypeError: split() got an unexpected keyword argument 'max_split'. Did you mean 'maxsplit'?

(由 Pablo Galindo Salgado 和 Shantanu Jain 在 gh-107944 [https://github.com/python/cpython/issues/107944] 中贡献。)

自由线程的 CPython

现在 CPython 具有对运行于禁用 global interpreter lock (GIL) 的自由线程模式的实验性支持。 这是一个实验性的特性因而默认是不启用的。 自由线程模式需要一个不同的可执行程序,通常名为 python3.13tpython3.13t.exe。 标记为 freethreaded 的预构建二进制文件可作为官方 WindowsmacOS 安装器的一部分被安装,或者可以附带 --disable-gil 选项使用源代码来构建 CPython。

自由线程模式的执行允许在可用的 CPU 核心上并行地运行线程从而充分利用可用的处理能力。 虽然并非所有软件都能自动从中受益,但在设计时将线程纳入考虑的程序在多核心硬件上运行速度会更快。 自由线程模式是实验性的 并且处于不断改进的过程中:预计会出现一些程序错误并且在单线程场景下出现明显的性能损失。 可以选择使用环境变量 PYTHON_GIL 或命令行选项 -X gil=1 让 CPython 的自由线程构建版支持在运行时启用 GIL。

要判断当前解释器是否支持自由线程,可检查 python -VVsys.version 是否包含 "experimental freethreading build"。 新的 sys.isgil_enabled() 函数可用于检查在运行进程中 GIL 是否确实被关闭。

C-API 扩展模块需要针对自由线程构建版专门进行构建。 支持在禁用 GIL 的情况下运行的扩展应当使用 Py_mod_gil 槽位。 使用单阶段初始化的扩展应当使用 PyUnstable_Module_SetGIL() 来指明它们是支支持在禁用 GIL 的情况下运行。 导入不使用这些机制的 C 扩展将导致 GIL 被启用,除非通过 PYTHON_GIL 环境变量或 -X gil=0 选项显式地禁用 GIL。 需要 pip 24.1 或更新的版本才能在自由线程构建版中安装带有 C 扩展的软件包。

这项工作成为可能要感谢许多个人和组织,包括针对 Python 和第三方项目测试并启用自由线程支持的庞大的贡献者社区。 重要的贡献者包括:Sam Gross, Ken Jin, Donghee Na, Itamar Oren, Matt Page, Brett Simmers, Dino Viehland, Carl Meyer, Nathan Goldbaum, Ralf Gommers, Lysandros Nikolaou 及其他许多人。 有许多贡献者受雇于 Meta,该公司提供了大量的工程资源来支持此项目。

参见

PEP 703 [https://peps.python.org/pep-0703/] "Making the Global Interpreter Lock Optional in CPython" 中包含了有关此项工作的理念和信息。

Porting Extension Modules to Support FreeThreading [https://py-free-threading.github.io/porting/]: 一份由社区维护的针对扩展开发者的移植指南。

实验性的即时 (JIT) 编译器

当 CPython 使用 --enable-experimental-jit 选项进行配置和构建时,会添加一个即时(JIT)编译器以加快某些 Python 程序的运行速度。 在 Windows 上,可使用 PCbuild/build.bat --experimental-jit 启用 JIT 或使用 --experimental-jit-interpreter 启用第 2 层级解释器。 构建要求和进一步的支持信息 包含在 [https://github.com/python/cpython/blob/main/Tools/jit/README.md] Tools/jit/README.md 中。

--enable-experimental-jit 选项接受这些(可选)值,如果不带可选值地预设 --enable-experimental-jit 则默认为 yes

  • no: 禁用整个第 2 层级和 JIT 管线。

  • yes: 启用 JIT。 要在运行时禁用 JIT,则传入环境变量 PYTHON_JIT=0

  • yes-off: 构建 JIT 但默认禁用它。 要在运行时启用 JIT,则传入环境变量 PYTHON_JIT=1

  • interpreter: 启用第 2 层级解释器但是禁用 JIT。 可以在运行时传入 PYTHON_JIT=0 来禁用该解释器。

其内部架构大致如下:

  • 我们将从特化的 第 1 层级字节码 开始。 请参阅 3.11 有什么新变化 了解详情。

  • 当第 1 层级字节码达到足够热度,它将被翻译为新的纯内部的中间表示形式 (IR),称为 第 2 层级 IR,有时也称为微操作码 ("uops")。

  • 第 2 层级 IR 使用与第 1 层级相同的基于栈的虚拟机,但其指令格式更适合被翻译为机器码。

  • 在第 2 层级 IR 被解释或翻译为机器码之前,我们会预先应用一些优化通路。

  • 虽然第 2 层级解释器存在,但它主要用于对优化管线的先前阶段进行调试。可通过为 Python 配置 --enable-experimental-jit=interpreter 选项启用第 2 层级解释器。

  • 启用 JIT 时,经优化的第 2 层级 IR 将被翻译为机器码后再执行。

  • 这个机器码翻译过程使用了名为 拷贝并打补丁 的技巧。 它没有运行时依赖,但增加了构建时对 LLVM 的依赖。

参见

PEP 744 [https://peps.python.org/pep-0744/]

(JIT 来自 Brandt Bucher 且受到 Haoran Xu 和 Fredrik Kjolstad 论文的启发。第 2 层级 IR 来自 Mark Shannon 和 Guido van Rossum。第 2 层级解释器来自 Ken Jin。)

针对 locals() 的已定义修改语义

在历史上,改变 locals() 的返回值的预期结果是留给具体的 Python 实现来定义的。 从 Python 3.13 开始, PEP 667 [https://peps.python.org/pep-0667/] 标准化了 CPython 对于大多数代码执行作用域的历史行为,但也将 已优化作用域 (函数、生成器、协程、推导式和生成器表达式) 修改为显式地返回当前已赋值的局部变量的独立快照,包括局部引用的在闭包中捕获的非局部变量。

在已优化作用域中对 locals() 语义的这项修改也会影响隐式地以 locals() 为目标的代码执行函数的默认行为,如果没有提供显式命名空间的话(例如 exec()eval() 等)。 在之前的版本中,在调用代码执行函数后是否可以通过调用 locals() 访问更改情况取决于具体的实现。 具体到 CPython 而言,此类代码通常会按预期工作,但有时可能会在基于其他代码(包括调试器和代码执行跟踪工具)的已优化作用域中失败,因为代码有可能重置该作用域中的共享快照。 现在,代码在已优化作用域中将始终针对局部变量的独立快照运行,因为在后续调用 locals() 时将永远看不到更改。 要访问在这些情况下所做的更改,现在必须将一个显式命名空间引用传递给相关的函数。 或者,也可以更新受影响的代码以使用更高层级的代码执行 API 返回结果代码命名空间(例如,当执行磁盘上的 Python 文件时使用 runpy.run_path() 函数)。

为确保调试器和类似工具能可靠地更新受到此变化影响的作用域中的局部变量,现在 FrameType.f_locals 将返回一个针对此种作用域中的帧的局部变量和在局部引用的非局部变量的直通写入代理对象,而不是返回一个非持续更新的具有规定义的运行时语义的共享 dict 实例。

请参阅 PEP 667 [https://peps.python.org/pep-0667/] 了解详情,包括相关的 C API 更改和弃用。 下文还针对受影响的 Python APIC API 提供了移植说明。

(PEP 和实现由 Mark Shannon 和 Tian Gao 在 gh-74929 [https://github.com/python/cpython/issues/74929] 中贡献。 文档更新由 Guido van Rossum 和 Alyssa Coghlan 提供。)

对移动平台的支持

PEP 730 [https://peps.python.org/pep-0730/]: iOS 现在是 PEP 11 [https://peps.python.org/pep-0011/] 所支持的平台,包括第 3 层级的 arm64-apple-iosarm64-apple-ios-simulator 等目标(分别为2013 年后的 iPhone 和 iPad 设备以及运行于 Apple silicon 硬件的 Xcode iOS 模拟器)。 x86_64-apple-ios-simulator (运行于较旧的 x86_64 硬件的 Xcode iOS 模拟器)不是第 3 层级的受支持平台,但也将尽可能地支持。 (PEP 撰写及实现由 Russell Keith-Magee 在 gh-114099 [https://github.com/python/cpython/issues/114099] 中贡献。).) PEP 738 [https://peps.python.org/pep-0738/]: Android 现在是 PEP 11 [https://peps.python.org/pep-0011/] 所支持的平台,包括位于第 3 层级的 aarch64-linux-androidx86_64-linux-android 等目标。 32 位的目标 arm-linux-androideabii686-linux-android 不是第 3 层级的受支持平台,但也将尽可能地支持。 (PEP 撰写及实现由 Malcolm Smith 在 gh-116622 [https://github.com/python/cpython/issues/116622] 中贡献。)

参见

PEP 730 [https://peps.python.org/pep-0730/], PEP 738 [https://peps.python.org/pep-0738/]

其他语言特性修改

  • 编译器现在将从文档字符串的每一行去除共有的前导空格。 这会减少 字节码缓存 的大小(例如 .pyc 文件),例如在 SQLAlchemy 2.0 的 sqlalchemy.orm.session 中文件大小将减少约 5%。 这项改变将影响各种使用了文档字符串的工具,如 doctest
  1. >>> def spam():
  2. ... """
  3. ... This is a docstring with
  4. ... leading whitespace.
  5. ...
  6. ... It even has multiple paragraphs!
  7. ... """
  8. ...
  9. >>> spam.__doc__
  10. '\nThis is a docstring with\n leading whitespace.\n\nIt even has multiple paragraphs!\n'

(由 Inada Naoki 在 gh-81283 [https://github.com/python/cpython/issues/81283] 中贡献。)

  • 类作用域内的 标注作用域 现在可以包含 lambda 和推导式。 位于类作用域内的推导式不会内联到其父作用域中。
  1. class C[T]:
  2. type Alias = lambda: T

(由 Jelle Zijlstra 在 gh-109118 [https://github.com/python/cpython/issues/109118] 和 gh-118160 [https://github.com/python/cpython/issues/118160] 中贡献。)

(由 Victor Stinner 在 gh-114570 [https://github.com/python/cpython/issues/114570] 中贡献。)

新增模块

改进的模块

argparse

array

ast

  • 现在 ast 模块中节点类型的构造器对其接受的参数要求更为严格,并在参数被省略时有更易理解的行为。

如果在构造实例时某个 AST 节点上的可选字段没有被作为参数包括在内,则该字段现在将被设为 None。 类似地,如果某个列表字段被省略,则该字段现在将被设为空列表,而如果某个 expr_context 字段被省略,则它将默认为 Load()。 (之前,在所有情况下,新构造的 AST 节点实例上的相应属性都将缺失。)

在所有其他情况下,当需要的参数被省略时,节点构造器将发出 DeprecationWarning。 这在 Python 3.15 中将会引发异常。 类似地,将关键字参数传入一个未映射到 AST 节点上的字段的构造器的做法现在已被弃用,并且在 Python 3.15 中将会引发异常。

这些更改将不会应用于用户自定义的 ast.AST 子类,除非该类选择通过设置 AST._field_types 映射的方式加入新的行为。

(由 Jelle Zijlstra 在 gh-105858 [https://github.com/python/cpython/issues/105858], gh-117486 [https://github.com/python/cpython/issues/117486] 和 gh-118851 [https://github.com/python/cpython/issues/118851] 中贡献。)

asyncio

对于任务分组在外部被取消时同时必须引发 ExceptionGroup 的情况,现在它将调用父任务的 cancel() 方法。 这样可以确保 CancelledError 会在下一次 await 时被引发,因此取消操作不会丢失。, so the cancellation is not lost.

这些更改的一个附加好处是现在任务组会保留取消操作计数 (cancelling())。

为了处理某些边界情况,现在 uncancel() 可以在取消操作计数达到零时重置未写入文档的 mustcancel 旗标。

(受到由 Arthur Tacca 在 gh-116720 [https://github.com/python/cpython/issues/116720] 中报告的问题的启发。)

base64

compileall

concurrent.futures

configparser

copy

任何用户自定义类也可以通过定义 __replace__() 方法来支持 copy.replace()。 (由 Serhiy Storchaka 在 gh-108751 [https://github.com/python/cpython/issues/108751] 中贡献。)

ctypes

  • 作为必要的内部重构的一个后果,内部元类的初始化现在将发生于 __init__ 中而不是 __new__ 中。 这会影响子类化这些内部元类以提供自定义初始化的项目。 一般而言:

    • 调用 super().__new__ 之后在 __new__ 中完成的自定义逻辑应当移至 __init__

    • 要创建一个类,需调用相应的元类,而不仅是该元类的 __new__ 方法。

请参阅 gh-124520 [https://github.com/python/cpython/issues/124520] 了解相关讨论和对某些受影响项目的修改的链接。

dbm

dis

doctest

email

enum

fractions

glob

importlib

这些函数将不再被弃用也不会被加入移除计划。 (由 Petr Viktorin 在 gh-116608 [https://github.com/python/cpython/issues/116608] 中贡献。)

io

ipaddress

itertools

marshal

math

  • 新增函数 fma() 可执行合并的乘法-加法运算。 此函数只需一轮操作即可计算 x * y + z,从而避免了任何中间步骤导致的精度损失。 它包装了 C99 所提供的 fma() 函数,并且遵从针对特殊情况的 IEEE 754 "fusedMultiplyAdd" 运算规范。 (由 Mark Dickinson 和 Victor Stinner 在 gh-73468 [https://github.com/python/cpython/issues/73468] 中贡献。)

mimetypes

mmap

multiprocessing

os

os.path

pathlib

pdb

queue

random

re

  • re.error 重命名为 PatternError 以改善准确性。 re.error 仍被保留用于向下兼容。

shutil

site

sqlite3

ssl

备注

VERIFY_X509_STRICT 可能会拒绝下层 OpenSSL 实现本来会接受的 RFC 5280 [https://datatracker.ietf.org/doc/html/rfc5280.html] 以前的证书或格式错误的证书。 虽然不建议禁用此功能,但你可以使用以下方式禁用它:

  1. import ssl
  2.  
  3. ctx = ssl.create_default_context()
  4. ctx.verify_flags &= ~ssl.VERIFY_X509_STRICT

(由 William Woodruff 在 gh-112389 [https://github.com/python/cpython/issues/112389] 贡献。)

statistics

subprocess

需要注意的是,当 close_fds 为 True 时(默认值),则将在 C 库提供了 posix_spawn_file_actions_addclosefrom_np() 时使用 posix_spawn(),这包括近期的 Linux, FreeBSD 和 Solaris 版本。 在 Linux,其性能应当与现有的 Linux vfork() 基础代码类似。

如果你需要强制 subprocess 绝不使用 posix_spawn() 可以将私有的控制节点 subprocess._USE_POSIX_SPAWN 设为 False。 如果你这样设置的话请在 issue tracker 中报告你的理由和平台相关的细节以便我们能够为大家改进 API 的选择逻辑。 (由 Jakub Kulik 在 gh-113117 [https://github.com/python/cpython/issues/113117] 中贡献。)