Python 3.12 有什么新变化

Python 3.12 有什么新变化

编者:

Adam Turner

本文介绍 Python 3.12 相比 3.11 增加的新特性。 Python 3.12 已于 2023 年 10 月 2 日发布。 要获取详细信息,可参阅 changelog。

参见

PEP 693 — Python 3.12 发布计划

摘要 — 发布重点

Python 3.12 是 Python 编程语言的一个稳定发布版,包含一系列对语言和标准库的改变。 库的改变主要集中在清理已弃用的 API、易用性和正确性等方面。 值得注意的是,distutils 包已从标准库中移除。 os 和 pathlib 中的文件系统支持得到了许多改进,一些模块的性能也得到了提升。

语言的改变主要集中在可用性方面,如 f-字符串 的许多限制已被移除,而 ‘Did you mean …’ 提示消息继续得到改进。 新的 类型形参语法 和 type 语句提升了 泛型类型 和 类型别名 配合静态类型检查器使用时的效率。

本文并不试图提供所有新功能的完整规范说明,而是提供一个方便的概览。 如需了解完整细节,请参阅相应文档,如 标准库参考 和 语言参考。 如果你想了解某项改变的完整实现和设计理念,请参阅相应新特性的 PEP;但请注意一旦某项特性已完全实现则相应 PEP 通常不会再继续更新。


新的语法特性:

  • PEP 695,类型形参语法和 type 语句

新的语法特性:

解释器的改进:

对 Python 数据模型的改进:

标准库中的重大改进:

安全改进:

  • 用来自 HACL* 项目的经过正式验证的代码替代 SHA1, SHA3, SHA2-384, SHA2-512 和 MD5 的内置 hashlib 实现。 这些内置实现保留作为仅在当 OpenSSL 未提供它们时使用的回退选项。

C API 的改进:

  • PEP 697,不稳定 C API 层

  • PEP 683,永生对象

CPython 实现的改进:

  • PEP 709,推导式内联化

  • 对 Linux perf 性能分析器的 CPython 支持

  • 在受支持的平台上实现栈溢出保护

新的类型标注特性:

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

  • PEP 623: 在 Python 的 C API 中移除 Unicode 对象中的 wstr,使每个 str 对象的大小缩减至少 8 个字节。

  • PEP 632: 移除 distutils 包。 请参阅 迁移指南 了解有关替换其所提供的 API 的建议。 第三方 Setuptools 包将继续提供 distutils,如果你在 Python 3.12 及更高版本中仍然需要它的话。

  • gh-95299: 不在使用 venv 创建的虚拟环境中预装 setuptools。 这意味着 distutils、setuptools、pkg_resources 和 easy_install 默认将不再可用;要访问这些工具请在 激活的 虚拟环境中运行 pip install setuptools。

  • 移除了 asynchat、asyncore 和 imp 模块,以及一些 unittest.TestCase 方法别名。

新的特性

PEP 695: 类型形参语法

PEP 484 下的泛型类和函数是使用详细语法声明的,这使得类型参数的范围不明确,并且需要显式声明变化。

PEP 695 引入了一种新的、更紧凑、更明确的方式来创建 泛型类 和 函数:

def max[T](args: Iterable[T]) -> T:
    ...

class list[T]:
    def __getitem__(self, index: int, /) -> T:
        ...

    def append(self, element: T) -> None:
        ...

此外,该 PEP 引入了一种新的方法来使用 type 语句声明 类型别名,该语句会创建 TypeAliasType 的实例:

type Point = tuple[float, float]

类型别名也可以是 generic:

type Point[T] = tuple[T, T]

新语法允许声明 TypeVarTuple 和 ParamSpec 形参,以及带边界或约束的 TypeVar 形参:

type IntFunc[**P] = Callable[P, int]  # ParamSpec
type LabeledTuple[*Ts] = tuple[str, *Ts]  # TypeVarTuple
type HashableSequence[T: Hashable] = Sequence[T]  # 带边界的 TypeVar
type IntOrStrSequence[T: (int, str)] = Sequence[T]  # 带约束的 TypeVar

类型别名的值以及通过此语法创建的类型变量的边界和约束仅在需要时才进行求值 (参见 惰性求值)。 这意味着类型别名可以引用稍后在文件中定义的其他类型。

通过类型参数列表声明的类型参数在声明的作用域和任何嵌套的作用域内都可见,但在外部作用域内不可见。 例如,它们可以用于泛型类的方法的类型注解或类体中。 但是,在定义类之后,不能在模块范围中使用它们。 有关类型参数的运行时语义的详细描述,请参见 类型形参列表。

为了支持这些作用域定义,引入了一种新的作用域,即 标注作用域。 标注作用域的行为在很大程度上类似于函数作用域,但与封闭类作用作用域的交互方式不同。 在 Python 3.13 中,标注 也将在标注作用域中进行求值。

更多细节请参见 PEP 695。

(PEP由 Eric Traut 撰写。 由 Jelle Zijlstra、Eric Traut 和其他人在 gh-103764 中实现。)

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

PEP 701 取消了对 f-字符串 使用的一些限制。 f-字符串内部的表达式部分现在可以是任何有效的 Python 表达式,包括重用了与标记 f-字符串本身相同的引号的字符串、多行表达式、注释、反斜杠以及 unicode 转义序列。 让我们详细介绍一下:

  • 引号重用:在 Python 3.11 中,重用与标记 f-字符串本身相同的引号会引发 SyntaxError,迫使用户使用其他可用的引号(如在 f-字符串使用单引号时使用双引号或三重引号)。 在 Python 3.12 中,你现在可以这样做了:
>>> 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'
  • 多行表达式和注释:在 Python 3.11 中,f-字符串表达式必须在一行中完成定义,即使 f-字符串中的表达式在正常情况下可以跨多行(如在多行中定义的列表字面值),这使得它们更难被读懂。 在 Python 3.12 中,你现在可以定义跨越多行的 f-字符串并添加内联注释:
>>> 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'
  • 反斜杠和 unicode 字符:在 Python 3.12 之前,f-字符串表达式不能包含任何 \ 字符。 这也影响了 unicode 转义序列 (如 \N{snowman}),因为这些序列包含 \N 部分,而这部分以前不能作为 f-字符串表达式组件的一部分。 现在,你可以这样定义表达式:
>>> 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。

实现此特性的一个正面的附带影响是(通过使用 PEG 解析器 来解析 f-字符串),现在 f-字符串的错误消息会更加精确,包括错误的确切位置。例如,在 Python 3.11 中,下面的 f-字符串将引发一个 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 684: 每解释器 GIL

PEP 684 引入了每解释器 GIL,使得现在可以创建带有单独的每解释器 GIL 的子解释器。 这将允许 Python 程序充分利用多个 CPU 核心。 此特性目前仅能通过 C-API 使用,不过相应的 Python API 预计将在 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;
}
/* 新解释器现已在当前线程中激活。 */

有关如何将For further examples how to use the C-API 用于具有每解释器 GIL 的子解释器的更多示例,请参见 Modules/_xxsubinterpretersmodule.c。

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

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

PEP 669 定义了一个新的 API 用于性能分析器、调试器和其他在 CPython 中监控事件的工具。 它覆盖了大范围的事件,包括调用、返回、行、异常、跳转等等。 这意味着你将只为你所使用的东西付出开销,提供了对近乎零开销的调试器和覆盖工具的支持。 请参阅 sys.monitoring 了解详情。

(由 Mark Shannon 在 gh-103082 中贡献。)

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

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

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

PEP 709:推导式内联

字典、列表和集合推导式现在都是内联的,而不是为每次执行推导式都创建一个新的一次性函数对象。 这样可以将推导式的执行速度提高最多两倍。 更多细节请参阅 PEP 709。

推导式迭代变量将保持隔离而不会覆盖外作用域中的同名变量,在离开推导式后也不再可见。 内联确实会导致一些可见的行为变化:

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

  • 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]。

(由 Carl Meyer 和 Vladimir Matveev 在 PEP 709 中贡献。)

改进的错误消息

  • 当引发的 NameError 传播到最高层级时,解释器显示的错误消息可能将标准库中的模块作为建议的一部分。 (由 Pablo Galindo 在 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'?
  • 改进针对实例的 NameError 异常的错误建议。 现在如果在方法中引发了 NameError 而实例具有与异常中的名称完全相同的属性,建议将会包括 self.<NAME> 而不是方法作用域中最接近的匹配项。 (由 Pablo Galindo 在 gh-99139 中贡献。)
>>> class A:
...    def __init__(self):
...        self.blech = 1
...
...    def foo(self):
...        somethin = blech
...
>>> A().foo()
Traceback (most recent call last):
  File "<stdin>", line 1
    somethin = blech
               ^^^^^
NameError: name 'blech' is not defined. Did you mean: 'self.blech'?
  • 改进了当用户输入 import x from y 而不是 from y import x 时产生的 SyntaxError 错误消息。 (由 Pablo Galindo 在 gh-98931 中贡献。)
>>> import a.y.z from b.y.z
Traceback (most recent call last):
  File "<stdin>", line 1
    import a.y.z from b.y.z
    ^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: Did you mean to use 'from ... import ...' instead?
  • 由失败的 from <module> import <name> 语句引发的 ImportError 异常现在会包括根据 <module> 中的可用名称对 <name> 的值提出的建议。 (由 Pablo Galindo 在 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'?

有关类型提示的新增特性

本节介绍了影响 类型提示 和 typing 模块的主要更改。

PEP 692: 使用 TypedDict 进行更精确的 **kwargs 类型标注

在函数签名中的 **kwargs 类型标注(由 PEP 484 引入)只允许在所有 **kwargs 都属于同一类型的情况下进行有效标注。

PEP 692 通过依赖类型化的字典规定了一种更精确的针对 **kwargs 的类型标注方式:

from typing import TypedDict, Unpack

class Movie(TypedDict):
  name: str
  year: int

def foo(**kwargs: Unpack[Movie]): ...

更多细节请参见 PEP 692。

(由 Franek Magiera 在 gh-103629 中贡献。)

PEP 698:覆盖静态类型的装饰器

一个新的装饰器 typing.override() 已添加到 typing 模块中。 它向类型检查器指示该方法旨在重写超类中的方法。 这允许类型检查器在打算重写基类中的某个方法实际上没有重写的情况下捕获错误。

示例:

from typing import override

class Base:
  def get_color(self) -> str:
    return "blue"

class GoodChild(Base):
  @override  # 正确:重写 Base.get_color
  def get_color(self) -> str:
    return "yellow"

class BadChild(Base):
  @override  # 类型检查错误:不能重写 Base.get_color
  def get_colour(self) -> str:
    return "red"

更多细节参见 PEP 698。

(由 Steven Troxler 在 gh-101561 中贡献。)

其他语言特性修改

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

  • 不是有效转义序列的反斜杠加字符组合现在会生成 SyntaxWarning,而不是 DeprecationWarning。 例如,re.compile("\d+\.\d+") 现在会发出 SyntaxWarning ("\d" 是一个无效的转义序列,请使用原始字符串来表示正则表达式: re.compile(r"\d+\.\d+"))。 在未来的 Python 版本中,最终将引发 SyntaxError,而不是 SyntaxWarning。 (由 Victor Stinner 在 gh-98401 中贡献。)

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

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

  • 在类或类型对象的 __set_name__ 方法中引发的异常不再由 RuntimeError 来包装。 上下文信息将作为 PEP 678 注释添加到异常中。 (由 Irit Katriel 在 gh-77757 中贡献。)

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

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

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

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

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

  • sum() 现在使用 Neumaier 求和算法以改善对浮点数或混合了整数和浮点数时求和运算的准确性和可换算性。 (由 Raymond Hettinger 在 gh-100425 中贡献。)

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

  • tarfile 中的提取方法和 shutil.unpack_archive() 有一个新的 filter 参数,它允许限制可能令人惊讶或危险的 tar 功能,例如在目标目录之外创建文件。 相关细节请参阅 tarfile 提取过滤器。 在 Python 3.14 中。默认值将切换为 'data'。 (由 Petr Viktorin 在 PEP 706 中贡献。)

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

  • 通过新的环境变量 PYTHONPERFSUPPORT 和命令行选项 -X perf 以及新的 sys.activate_stack_trampoline(), sys.deactivate_stack_trampoline() 和 sys.is_stack_trampoline_active() 函数添加了对 perf 性能分析器的支持。 (由 Pablo Galindo 设计。 由 Pablo Galindo 和 Christian Heimes 在 gh-96123 中贡献并包含来自 Gregory P. Smith [Google] 和 Mark Shannon 的帮助。)

新增模块

  • 无。

改进的模块

array

asyncio

calendar

csv

dis

  • 伪指令操作码(由编译器使用但不会出现在可执行字节码中)现在将暴露在 dis 模块中。 HAVE_ARGUMENT 仍然与实际的操作码相关,但对伪指令来说没有用处。 请改用新的 dis.hasarg 多项集。 (由 Irit Katriel 在 gh-94216 中贡献。)

  • 添加了 dis.hasexc 多项集来表示设置异常处理器的指令。 (由 Irit Katriel 在 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 已被弃用。 (由 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

  • tokenize 模块包括了 PEP 701 所引入的更改。 (由 Marta Gómez Macías 和 Pablo Galindo 在 gh-102856 中贡献。) 请参阅 移植到 Python 3.12 了解有关对 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)  # 无变化,虽然 HasX 现在也有一个 "y" 属性
True

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

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 在 gh-48330 中贡献。)

uuid

(本文超出站点单文档体积上限,已按小节截断)