Python 3.10 新变化

编辑:

Pablo Galindo Salgado

本文档解释了 Python 3.10 相对于 3.9 的新功能。Python 3.10 于 2021 年 10 月 4 日发布。有关完整详细信息,请参阅 变更日志

摘要 – 发布亮点

新语法功能

  • PEP 634, 结构化模式匹配:规范

  • PEP 635, 结构化模式匹配:动机和原理

  • PEP 636, 结构化模式匹配:教程

  • bpo-12782, 括号包围的上下文管理器现已正式允许。

标准库新功能

  • PEP 618, 向 zip 添加可选的长度检查。

解释器的改进

  • PEP 626, 为调试和其他工具提供精确的行号。

新的类型特性

  • PEP 604, 允许使用 X | Y 形式书写联合类型

  • PEP 612, 参数规范变量

  • PEP 613, 显式类型别名

  • PEP 647, 用户定义的类型守卫

重要的弃用、移除或限制

  • PEP 644, 要求 OpenSSL 1.1.1 或更高版本

  • PEP 632, 弃用 distutils 模块。

  • PEP 623, 弃用 PyUnicodeObject 中的 wstr 成员并为此做准备。

  • PEP 624, 移除 Py_UNICODE 编码器 API

  • PEP 597, 添加可选的 EncodingWarning

新功能

括号包围的上下文管理器

现在支持使用包含括号来跨越多行延续上下文管理器。这允许以与之前导入语句类似的方式格式化多行中的长上下文管理器集合。例如,以下所有示例现在都有效

with (CtxManager() as example):
    ...

with (
    CtxManager1(),
    CtxManager2()
):
    ...

with (CtxManager1() as example,
      CtxManager2()):
    ...

with (CtxManager1(),
      CtxManager2() as example):
    ...

with (
    CtxManager1() as example1,
    CtxManager2() as example2
):
    ...

在包含组的末尾使用尾随逗号也是可能的

with (
    CtxManager1() as example1,
    CtxManager2() as example2,
    CtxManager3() as example3,
):
    ...

这种新语法利用了新解析器的非 LL(1) 功能。有关更多详细信息,请参阅 PEP 617

(由 Guido van Rossum、Pablo Galindo 和 Lysandros Nikolaou 在 bpo-12782bpo-40334 中贡献。)

更好的错误消息

SyntaxErrors

当解析包含未闭合括号或方括号的代码时,解释器现在会包含未闭合方括号或括号的位置,而不是显示 *SyntaxError: unexpected EOF while parsing* 或指向某个不正确的位置。例如,考虑以下代码(注意未闭合的‘{‘)

expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
            38: 4, 39: 4, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 54: 6,
some_other_code = foo()

旧版本解释器报告了令人困惑的语法错误位置

File "example.py", line 3
    some_other_code = foo()
                    ^
SyntaxError: invalid syntax

但在 Python 3.10 中,会发出更具信息性的错误

File "example.py", line 1
    expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
               ^
SyntaxError: '{' was never closed

类似地,涉及未闭合字符串字面量(单引号和三引号)的错误现在会指向字符串的开头,而不是报告 EOF/EOL。

这些改进受到了 PyPy 解释器先前工作的启发。

(由 Pablo Galindo 在 bpo-42864 和 Batuhan Taskaya 在 bpo-40176 中贡献。)

由解释器引发的 SyntaxError 异常现在将突出显示构成语法错误的表达式的完整错误范围,而不仅仅是检测到问题的位置。这样,在 Python 3.10 之前,而不是显示:

>>> foo(x, z for z in range(10), t, w)
  File "<stdin>", line 1
    foo(x, z for z in range(10), t, w)
           ^
SyntaxError: Generator expression must be parenthesized

现在 Python 3.10 将显示异常为:

>>> foo(x, z for z in range(10), t, w)
  File "<stdin>", line 1
    foo(x, z for z in range(10), t, w)
           ^^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized

此改进由 Pablo Galindo 在 bpo-43914 中贡献。

已包含大量新的 SyntaxError 异常的专用消息。其中一些最值得注意的是:

  • 块前缺少 ‘:’

    >>> if rocket.position > event_horizon
      File "<stdin>", line 1
        if rocket.position > event_horizon
                                          ^
    SyntaxError: expected ':'
    

    (由 Pablo Galindo 在 bpo-42997 中贡献。)

  • 推导式目标中缺少括号的元组

    >>> {x,y for x,y in zip('abcd', '1234')}
      File "<stdin>", line 1
        {x,y for x,y in zip('abcd', '1234')}
         ^
    SyntaxError: did you forget parentheses around the comprehension target?
    

    (由 Pablo Galindo 在 bpo-43017 中贡献。)

  • 集合字面量中以及表达式之间缺少逗号

    >>> items = {
    ... x: 1,
    ... y: 2
    ... z: 3,
      File "<stdin>", line 3
        y: 2
           ^
    SyntaxError: invalid syntax. Perhaps you forgot a comma?
    

    (由 Pablo Galindo 在 bpo-43822 中贡献。)

  • 多个异常类型未用括号括起来

    >>> try:
    ...     build_dyson_sphere()
    ... except NotEnoughScienceError, NotEnoughResourcesError:
      File "<stdin>", line 3
        except NotEnoughScienceError, NotEnoughResourcesError:
               ^
    SyntaxError: multiple exception types must be parenthesized
    

    (由 Pablo Galindo 在 bpo-43149 中贡献。)

  • 字典字面量中缺少 ‘:’ 和值

    >>> values = {
    ... x: 1,
    ... y: 2,
    ... z:
    ... }
      File "<stdin>", line 4
        z:
         ^
    SyntaxError: expression expected after dictionary key and ':'
    
    >>> values = {x:1, y:2, z w:3}
      File "<stdin>", line 1
        values = {x:1, y:2, z w:3}
                            ^
    SyntaxError: ':' expected after dictionary key
    

    (由 Pablo Galindo 在 bpo-43823 中贡献。)

  • 不带 except 或 finally 块的 try 块

    >>> try:
    ...     x = 2
    ... something = 3
      File "<stdin>", line 3
        something  = 3
        ^^^^^^^^^
    SyntaxError: expected 'except' or 'finally' block
    

    (由 Pablo Galindo 在 bpo-44305 中贡献。)

  • 在比较中使用 ‘=’ 而不是 ‘==’

    >>> if rocket.position = event_horizon:
      File "<stdin>", line 1
        if rocket.position = event_horizon:
                           ^
    SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='?
    

    (由 Pablo Galindo 在 bpo-43797 中贡献。)

  • 在 f-string 中使用 ‘*’

    >>> f"Black holes {*all_black_holes} and revelations"
      File "<stdin>", line 1
        (*all_black_holes)
         ^
    SyntaxError: f-string: cannot use starred expression here
    

    (由 Pablo Galindo 在 bpo-41064 中贡献。)

IndentationErrors

许多 IndentationError 异常现在提供了更多关于期望缩进的块类型的上下文,包括语句的位置

>>> def foo():
...    if lel:
...    x = 2
  File "<stdin>", line 3
    x = 2
    ^
IndentationError: expected an indented block after 'if' statement in line 2

AttributeErrors

打印 AttributeError 时,PyErr_Display() 将提供与引发异常的对象中相似的属性名称的建议

>>> collections.namedtoplo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'collections' has no attribute 'namedtoplo'. Did you mean: namedtuple?

(由 Pablo Galindo 在 bpo-38530 中贡献。)

警告

请注意,如果未调用 PyErr_Display() 来显示错误,这可能发生在某些 REPL(如 IPython)中,这种情况将无效。这是某些 REPL(如 IPython)中常见的情况。

NameErrors

打印解释器引发的 NameError 时,PyErr_Display() 将提供与引发异常的函数中相似的变量名称的建议

>>> schwarzschild_black_hole = None
>>> schwarschild_black_hole
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'schwarschild_black_hole' is not defined. Did you mean: schwarzschild_black_hole?

(由 Pablo Galindo 在 bpo-38530 中贡献。)

警告

请注意,如果未调用 PyErr_Display() 来显示错误,这可能发生在某些 REPL(如 IPython)中,这种情况将无效。这是某些 REPL(如 IPython)中常见的情况。

PEP 626:为调试和其他工具提供精确的行号

PEP 626 为调试、分析和覆盖率工具带来了更精确和可靠的行号。将为执行的所有代码行生成正确的行号的跟踪事件,并且仅为已执行的代码行生成。

帧对象的 f_lineno 属性将始终包含预期的行号。

代码对象的 co_lnotab 属性已被弃用,并将在 3.12 中移除。需要从偏移量转换为行号的代码应改用新的 co_lines() 方法。

PEP 634:结构化模式匹配

结构化模式匹配已添加,形式为 *match 语句* 和 *case 语句*,其中包含具有关联操作的模式。模式包括序列、映射、基本数据类型以及类实例。模式匹配使程序能够从复杂数据类型中提取信息,根据数据结构进行分支,并根据不同形式的数据应用特定操作。

语法和操作

模式匹配的一般语法是

match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>

match 语句接受一个表达式,并将其值与按顺序提供的、由一个或多个 case 块组成的模式进行比较。具体来说,模式匹配通过以下方式工作:

  1. 使用具有类型和形状的数据(*subject*)

  2. 在 *match* 语句中计算 *subject*

  3. 从上到下将 subject 与 *case* 语句中的每个模式进行比较,直到确认匹配。

  4. 执行与已确认匹配的模式关联的操作

  5. 如果未确认完全匹配,则使用最后一个 case,即通配符 *_*(如果提供了)。如果未确认完全匹配且不存在通配符 case,则整个 match 块将不执行任何操作。

声明式方法

读者可能通过将主体(数据对象)与字面量(模式)匹配的简单示例,在 C、Java 或 JavaScript(以及许多其他语言)的 switch 语句中了解模式匹配。通常,switch 语句用于将对象/表达式与包含字面量的 case 语句进行比较。

在 Scala 和 Elixir 等语言中可以找到更强大的模式匹配示例。通过结构化模式匹配,方法是“声明式”的,并明确说明数据匹配的条件(模式)。

虽然可以使用使用嵌套“if”语句的“命令式”一系列指令来完成类似于结构化模式匹配的任务,但它不如“声明式”方法清晰。相反,“声明式”方法说明了匹配要满足的条件,并且通过其显式模式更具可读性。虽然结构化模式匹配可以用于其最简单的形式,即在 case 语句中将变量与字面量进行比较,但它对 Python 的真正价值在于其对 subject 的类型和形状的处理。

简单模式:匹配字面量

让我们看一个模式匹配最简单的形式的例子:一个值(subject)与几个字面量(patterns)进行匹配。在下面的示例中,*status* 是 match 语句的主题。模式是每个 case 语句,其中字面量代表请求状态码。在匹配后执行与 case 关联的操作

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"
        case _:
            return "Something's wrong with the internet"

如果将状态码 418 传递给上面的函数,将返回“I’m a teapot”。如果将状态码 500 传递给上面的函数,将匹配带有 *_* 的 case 语句作为通配符,并返回“Something’s wrong with the internet”。请注意最后一个块:变量名 *_* 用作*通配符*,并确保 subject 始终匹配。*_* 的使用是可选的。

您可以使用 *|*(“或”)将多个字面量组合在一个模式中

case 401 | 403 | 404:
    return "Not allowed"
无通配符的行为

如果我们通过删除最后一个 case 块来修改上面的示例,示例将变成

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"

如果不使用 *_* 在 case 语句中,则可能不存在匹配。如果不存在匹配,则行为是无操作。例如,如果传递状态码 500,则会发生无操作。

带字面量和变量的模式

模式可以看起来像解包赋值,并且可以使用模式来绑定变量。在此示例中,数据点可以解包为其 x 坐标和 y 坐标

# point is an (x, y) tuple
match point:
    case (0, 0):
        print("Origin")
    case (0, y):
        print(f"Y={y}")
    case (x, 0):
        print(f"X={x}")
    case (x, y):
        print(f"X={x}, Y={y}")
    case _:
        raise ValueError("Not a point")

第一个模式包含两个字面量 *(0, 0)*,可以将其视为上面字面量模式的扩展。接下来的两个模式结合了一个字面量和一个变量,该变量*绑定*了 subject(*point*)中的值。第四个模式捕获了两个值,这使得它在概念上类似于解包赋值 *(x, y) = point*。

模式和类

如果您使用类来组织数据,则可以使用类名后跟类似于构造函数的参数列表作为模式。此模式能够将实例属性捕获到变量中

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def location(point):
    match point:
        case Point(x=0, y=0):
            print("Origin is the point's location.")
        case Point(x=0, y=y):
            print(f"Y={y} and the point is on the y-axis.")
        case Point(x=x, y=0):
            print(f"X={x} and the point is on the x-axis.")
        case Point():
            print("The point is located somewhere else on the plane.")
        case _:
            print("Not a point")
带位置参数的模式

您可以将位置参数与提供属性顺序的某些内置类(例如 dataclasses)一起使用。您还可以通过在类中设置 *__match_args__* 特殊属性来为模式中的属性定义特定位置。如果它设置为(“x”,“y”),则以下模式都是等效的(并且都将 *y* 属性绑定到 *var* 变量)

Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)

嵌套模式

模式可以任意嵌套。例如,如果我们的数据是点的简短列表,则可以这样匹配

match points:
    case []:
        print("No points in the list.")
    case [Point(0, 0)]:
        print("The origin is the only point in the list.")
    case [Point(x, y)]:
        print(f"A single point {x}, {y} is in the list.")
    case [Point(0, y1), Point(0, y2)]:
        print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
    case _:
        print("Something else is found in the list.")

复杂模式和通配符

到目前为止,示例在最后一个 case 语句中仅使用了 *_*。通配符可以在更复杂的模式中使用,例如 *('error', code, _)*。例如

match test_variable:
    case ('warning', code, 40):
        print("A warning has been received.")
    case ('error', code, _):
        print(f"An error {code} occurred.")

在上面的情况下,*test_variable* 将匹配 ('error', code, 100) 和 ('error', code, 800)。

Guard

我们可以向模式添加一个 *if* 子句,称为“guard”。如果 guard 为 false,*match* 将继续尝试下一个 case 块。请注意,值捕获发生在 guard 评估之前

match point:
    case Point(x, y) if x == y:
        print(f"The point is located on the diagonal Y=X at {x}.")
    case Point(x, y):
        print(f"Point is not on the diagonal.")

其他主要功能

其他几个主要功能

  • 与解包赋值类似,元组和列表模式的含义完全相同,并且实际上匹配任意序列。技术上,subject 必须是序列。因此,一个重要的例外是模式不匹配迭代器。此外,为了防止常见错误,序列模式不匹配字符串。

  • 序列模式支持通配符:*[*x*, *y*, ***rest**]* 和 *(*x*, *y*, ***rest**)* 的工作方式类似于解包赋值中的通配符。* 后面的名称也可以是 *_*,因此 *(*x*, *y*, ***_*)* 匹配至少有两个项的序列,而不绑定其余项。

  • 映射模式:*{"bandwidth": b, "latency": l}* 从 dict 中捕获 *"bandwidth"* 和 *"latency"* 的值。与序列模式不同,额外的键会被忽略。也支持通配符 ***rest**。(*但 **_* 是多余的,因此不允许*)。

  • 可以使用 *as* 关键字捕获子模式

    case (Point(x1, y1), Point(x2, y2) as p2): ...
    

    这将像您期望的那样绑定 x1、y1、x2、y2(不带 *as* 子句),并将 p2 绑定到 subject 的第二个项的整个部分。

  • 大多数字面量通过相等性进行比较。但是,单例 *True*、*False* 和 *None* 通过身份进行比较。

  • 命名常量可以在模式中使用。这些命名常量必须是点分名称,以防止常量被解释为捕获变量

    from enum import Enum
    class Color(Enum):
        RED = 0
        GREEN = 1
        BLUE = 2
    
    color = Color.GREEN
    match color:
        case Color.RED:
            print("I see red!")
        case Color.GREEN:
            print("Grass is green")
        case Color.BLUE:
            print("I'm feeling the blues :(")
    

有关完整规范,请参阅 PEP 634。动机和原理在 PEP 635 中,更长的教程在 PEP 636 中。

可选的 EncodingWarningencoding="locale" 选项

TextIOWrapperopen() 的默认编码是平台和区域设置相关的。由于 UTF-8 在大多数 Unix 平台上使用,因此在打开 UTF-8 文件(例如 JSON、YAML、TOML、Markdown)时省略 *encoding* 选项是一个非常常见的错误。例如

# BUG: "rb" mode or encoding="utf-8" should be used.
with open("data.json") as f:
    data = json.load(f)

为了查找这种类型的错误,添加了一个可选的 EncodingWarning。当 *sys.flags.warn_default_encoding* 为 true 且使用特定于区域设置的默认编码时,会发出此警告。

添加了 *-X warn_default_encoding* 选项和 PYTHONWARNDEFAULTENCODING 来启用警告。

有关更多信息,请参阅 文本编码

其他语言更改

  • *int* 类型有一个新方法 *int.bit_count()*,它返回给定整数的二进制展开中 1 的数量,也称为族计数。(由 Niklas Fiekas 在 bpo-29882 中贡献。)

  • *dict.keys()*、*dict.values()* 和 *dict.items()* 返回的视图现在都有一个 *mapping* 属性,该属性提供一个包装原始字典的 *types.MappingProxyType* 对象。(由 Dennis Sweeney 在 bpo-40890 中贡献。)

  • PEP 618:*zip()* 函数现在有一个可选的 *strict* 标志,用于要求所有可迭代对象具有相同的长度。

  • 接受整数参数的内置和扩展函数不再接受 *Decimal*、*Fraction* 和其他只能通过损失(例如,具有 *__int__* 方法但没有 *__index__* 方法)转换为整数的对象。(由 Serhiy Storchaka 在 bpo-37999 中贡献。)

  • 如果 *object.__ipow__()* 返回 *NotImplemented*,则该运算符将正确地回退到 *object.__pow__()* 和 *object.__rpow__()*,如预期。(由 Alex Shkop 在 bpo-38302 中贡献。)

  • 赋值表达式现在可以在不带括号的情况下用于集合字面量和集合推导式,以及在序列索引(但不是切片)中使用。

  • 函数具有新的 *__builtins__* 属性,用于在函数执行时查找内置符号,而不是查找 *__globals__['__builtins__']*。如果存在,则该属性从 *__globals__["__builtins__"]* 初始化,否则从当前内置项初始化。(由 Mark Shannon 在 bpo-42990 中贡献。)

  • 添加了两个新的内置函数 — *aiter()* 和 *anext()*,分别作为 *iter()* 和 *next()* 的异步对应项。(由 Joshua Bronson、Daniel Pope 和 Justin Wang 在 bpo-31861 中贡献。)

  • 静态方法(*@staticmethod*)和类方法(*@classmethod*)现在继承方法属性(*__module__*、*__name__*、*__qualname__*、*__doc__*、*__annotations__*),并具有新的 *__wrapped__* 属性。此外,静态方法现在可以像常规函数一样调用。(由 Victor Stinner 在 bpo-43682 中贡献。)

  • 对于复杂目标(PEP 526 定义的 *simple name* 目标之外的所有内容),使用 *from __future__ import annotations* 时,注解不再产生任何运行时效果。(由 Batuhan Taskaya 在 bpo-42737 中贡献。)

  • 类和模块对象现在按需惰性创建空注解字典。为了向后兼容,注解字典存储在对象的 *__dict__* 中。这改进了处理 *__annotations__* 的最佳实践;有关更多信息,请参阅 注解最佳实践。(由 Larry Hastings 在 bpo-43901 中贡献。)

  • 在 *from __future__ import annotations* 下,包含 *yield*、*yield from*、*await* 或命名表达式的注解现已被禁止,因为它们会产生副作用。(由 Batuhan Taskaya 在 bpo-42725 中贡献。)

  • 未绑定变量、*super()* 和其他可能改变符号表处理的表达式(如注解)现在在 *from __future__ import annotations* 下被渲染为无效。(由 Batuhan Taskaya 在 bpo-42725 中贡献。)

  • *float* 类型和 *decimal.Decimal* 类型的 NaN 值的哈希现在取决于对象标识。以前,它们总是哈希为 0,即使 NaN 值彼此不相等。这在创建包含多个 NaN 的字典和集合时,由于过度的哈希冲突,可能导致二次方运行时间行为。(由 Raymond Hettinger 在 bpo-43475 中贡献。)

  • 删除 *__debug__* 常量时将引发 *SyntaxError*(而不是 *NameError*)。(由 Donghee Na 在 bpo-45000 中贡献。)

  • *SyntaxError* 异常现在具有 *end_lineno* 和 *end_offset* 属性。如果未确定,它们将为 *None*。(由 Pablo Galindo 在 bpo-43914 中贡献。)

新模块

  • 无。

改进的模块

asyncio

添加了 missing *asyncio.loop.connect_accepted_socket()* 方法。(由 Alex Grönholm 在 bpo-41332 中贡献。)

argparse

argparse 帮助中将误导性短语“optional arguments”替换为“options”。如果某些测试依赖于精确的输出匹配,则可能需要进行调整。(由 Raymond Hettinger 在 bpo-9694 中贡献。)

array

*array.array* 的 *index()* 方法现在具有可选的 *start* 和 *stop* 参数。(由 Anders Lorentsen 和 Zackery Spytz 在 bpo-31956 中贡献。)

asynchat, asyncore, smtpd

自 Python 3.6 起,这些模块已在其模块文档中被标记为已弃用。现在已为这三个模块添加了导入时 *DeprecationWarning*。

base64

添加了 *base64.b32hexencode()* 和 *base64.b32hexdecode()* 以支持带扩展十六进制字母的 Base32 编码。

bdb

添加了 *clearBreakpoints()* 方法以重置所有设置的断点。(由 Irit Katriel 在 bpo-24160 中贡献。)

bisect

在 *bisect* 模块的 API 中添加了提供 *key* 函数的可能性。(由 Raymond Hettinger 在 bpo-4356 中贡献。)

codecs

添加了 *codecs.unregister()* 函数以注销编解码器搜索函数。(由 Hai Shi 在 bpo-41842 中贡献。)

collections.abc

用于 *collections.abc.Callable* 的*参数化泛型*的 *__args__* 现在与 *typing.Callable* 一致。*collections.abc.Callable* 泛型现在会展平类型参数,类似于 *typing.Callable* 目前所做的。这意味着 *collections.abc.Callable[[int, str], str]* 将具有 *(int, str, str)* 的 *__args__*;以前这是 *([int, str], str)*。为了允许此更改,*types.GenericAlias* 现在可以被子类化,并且在下标 *collections.abc.Callable* 类型时将返回子类。请注意,在 Python 3.9 中可能已默默通过的无效的 *collections.abc.Callable* 参数化形式可能会引发 *TypeError*。(由 Ken Jin 在 bpo-42195 中贡献。)

contextlib

添加了 *contextlib.aclosing()* 上下文管理器,以安全地关闭异步生成器和代表异步释放资源的对象。(由 Joongi Kim 和 John Belmonte 在 bpo-41229 中贡献。)

为 *contextlib.nullcontext()* 添加了异步上下文管理器支持。(由 Tom Gringauz 在 bpo-41543 中贡献。)

添加了 *AsyncContextDecorator*,用于支持将异步上下文管理器用作装饰器。

curses

ncurses 6.1 中添加的扩展颜色函数将被 *curses.color_content()*、*curses.init_color()*、*curses.init_pair()* 和 *curses.pair_content()* 透明使用。一个新函数 *curses.has_extended_color_support()* 指示底层 curses 库是否提供扩展颜色支持。(由 Jeffrey Kintscher 和 Hans Petter Jansson 在 bpo-36982 中贡献。)

如果底层 curses 库提供,*curses* 模块现在将公开 *BUTTON5_* 常量。(由 Zackery Spytz 在 bpo-39273 中贡献。)

dataclasses

__slots__

在 *dataclasses.dataclass()* 装饰器中添加了 *slots* 参数。(由 Yurii Karabas 在 bpo-42269 中贡献)

仅关键字参数字段

dataclasses 现在支持在生成的 __init__ 方法中仅通过关键字参数指定的字段。有多种方法可以指定仅关键字参数字段。

您可以声明所有字段都是仅关键字参数

from dataclasses import dataclass

@dataclass(kw_only=True)
class Birthday:
    name: str
    birthday: datetime.date

生成的 __init__ 方法的 *name* 和 *birthday* 都是仅关键字参数。

您可以按字段指定仅关键字参数

from dataclasses import dataclass, field

@dataclass
class Birthday:
    name: str
    birthday: datetime.date = field(kw_only=True)

这里只有 *birthday* 是仅关键字参数。如果您为单个字段设置了 *kw_only*,请注意由于仅关键字参数需要跟在非仅关键字参数之后,因此存在重新排序字段的规则。有关详细信息,请参阅完整的 dataclasses 文档。

您还可以指定 KW_ONLY 标记之后的所有字段都是仅关键字参数。这可能是最常见的用法

from dataclasses import dataclass, KW_ONLY

@dataclass
class Point:
    x: float
    y: float
    _: KW_ONLY
    z: float = 0.0
    t: float = 0.0

这里,*z* 和 *t* 是仅关键字参数,而 *x* 和 *y* 不是。(由 Eric V. Smith 在 bpo-43532 中贡献。)

distutils

整个 *distutils* 包已被弃用,将在 Python 3.12 中移除。其用于指定包构建的功能已被第三方包 *setuptools* 和 *packaging* 完全取代,并且大多数其他常用 API 在标准库的其他地方(如 *platform*、*shutil*、*subprocess* 或 *sysconfig*)都有提供。没有计划从 *distutils* 迁移任何其他功能,并且正在使用其他功能的应用程序应计划制作代码的私有副本。有关讨论,请参阅 PEP 632

Python 3.8 中已弃用的 *bdist_wininst* 命令已被移除。现在建议使用 *bdist_wheel* 命令在 Windows 上分发二进制包。(由 Victor Stinner 在 bpo-42802 中贡献。)

doctest

当模块未定义 *__loader__* 时,将回退到 *__spec__.loader*。(由 Brett Cannon 在 bpo-42133 中贡献。)

encodings

*encodings.normalize_encoding()* 现在会忽略非 ASCII 字符。(由 Hai Shi 在 bpo-39337 中贡献。)

enum

*Enum* 的 *__repr__()* 现在返回 *enum_name.member_name*,而 *__str__()* 现在返回 *member_name*。作为模块常量可用的标准库枚举具有 *module_name.member_name* 的 *repr()*。(由 Ethan Furman 在 bpo-40066 中贡献。)

添加了 *enum.StrEnum*,用于所有成员都是字符串的枚举。(由 Ethan Furman 在 bpo-41816 中贡献。)

fileinput

在 *fileinput.input()* 和 *fileinput.FileInput* 中添加了 *encoding* 和 *errors* 参数。(由 Inada Naoki 在 bpo-43712 中贡献。)

当 *mode* 为“r”且文件已压缩时,*fileinput.hook_compressed()* 现在返回 *TextIOWrapper* 对象,就像未压缩文件一样。(由 Inada Naoki 在 bpo-5758 中贡献。)

faulthandler

*faulthandler* 模块现在可以检测垃圾回收期间是否发生致命错误。(由 Victor Stinner 在 bpo-44466 中贡献。)

gc

为 *gc.get_objects()*、*gc.get_referrers()* 和 *gc.get_referents()* 添加了审计挂钩。(由 Pablo Galindo 在 bpo-43439 中贡献。)

glob

在 *glob.glob()* 和 *glob.iglob()* 中添加了 *root_dir* 和 *dir_fd* 参数,它们允许指定搜索的根目录。(由 Serhiy Storchaka 在 bpo-38144 中贡献。)

hashlib

hashlib 模块需要 OpenSSL 1.1.1 或更高版本。(由 Christian Heimes 在 PEP 644bpo-43669 中贡献。)

hashlib 模块对 OpenSSL 3.0.0 具有初步支持。(由 Christian Heimes 在 bpo-38820 和其他问题中贡献。)

纯 Python 回退的 *hashlib.pbkdf2_hmac()* 已弃用。未来 PBKDF2-HMAC 仅在 Python 已使用 OpenSSL 支持构建时可用。(由 Christian Heimes 在 bpo-43880 中贡献。)

hmac

hmac 模块现在内部使用 OpenSSL 的 HMAC 实现。(由 Christian Heimes 在 bpo-40645 中贡献。)

IDLE 和 idlelib

让 IDLE 在启动时(不带“-n”选项)调用 sys.excepthook()。之前用户自定义的钩子会被忽略。(由 Ken Hilton 在 bpo-43008 中贡献)

重新组织了设置对话框。将“常规”选项卡拆分为“窗口”和“Shell/编辑”选项卡。将扩展帮助菜单的帮助源移至“扩展”选项卡。为新选项腾出空间并缩短对话框。后者使对话框更适合小屏幕。(由 Terry Jan Reedy 在 bpo-40468 中贡献)将缩进空格设置从“字体”选项卡移至新的“窗口”选项卡。(由 Mark Roseman 和 Terry Jan Reedy 在 bpo-33962 中贡献)

上述更改已向后移植到 3.9 的维护版本。

添加了 Shell 侧边栏。将主提示符(“>>>”)移至侧边栏。将辅助提示符(“...”)添加到侧边栏。左键单击和可选的拖动可以选择一行或多行文本,类似于编辑器行号侧边栏。在选择文本行后右键单击会显示一个上下文菜单,其中包含“复制带提示符”选项。此选项将侧边栏中的提示符与选定文本中的行合并。此选项也出现在文本的上下文菜单中。(由 Tal Einat 在 bpo-37903 中贡献)

使用空格而非制表符缩进交互式代码。这使得交互式代码条目“看起来正确”。实现这一点是添加 shell 侧边栏的主要动机。(由 Terry Jan Reedy 在 bpo-37892 中贡献)

突出显示新的 软关键字 matchcase_ 在模式匹配语句中。但是,此突出显示并非完美,在某些罕见情况下会出现错误,包括 case 模式中的某些 _。(由 Tal Einat 在 bpo-44010 中贡献)

在 3.10 维护版本中新增。

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

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

importlib.metadata

importlib_metadata 4.6 功能对等(历史)。

importlib.metadata entry points 现在通过新的 importlib.metadata.EntryPoints 类,提供更友好的按组和名称选择入口点的体验。有关弃用和用法的更多信息,请参阅文档中的兼容性说明。

添加了 importlib.metadata.packages_distributions(),用于将顶层 Python 模块和包解析到它们的 importlib.metadata.Distribution

inspect

当模块未定义 *__loader__* 时,将回退到 *__spec__.loader*。(由 Brett Cannon 在 bpo-42133 中贡献。)

添加 inspect.get_annotations(),可安全地计算对象上定义的注解。它能处理各种类型对象的注解访问的怪癖,并且对它所检查的对象所做的假设非常少。 inspect.get_annotations() 还可以正确地取消字符串化注解。 inspect.get_annotations() 现在被认为是访问任何 Python 对象上定义的注解字典的最佳实践;有关处理注解最佳实践的更多信息,请参阅 注解最佳实践。相关地,inspect.signature()inspect.Signature.from_callable()inspect.Signature.from_function() 现在会调用 inspect.get_annotations() 来检索注解。这意味着 inspect.signature()inspect.Signature.from_callable() 现在也可以取消字符串化注解。(由 Larry Hastings 在 bpo-43817 中贡献)

itertools

添加 itertools.pairwise()。(由 Raymond Hettinger 在 bpo-38200 中贡献)

linecache

当模块未定义 *__loader__* 时,将回退到 *__spec__.loader*。(由 Brett Cannon 在 bpo-42133 中贡献。)

os

添加 os.cpu_count() 对 VxWorks RTOS 的支持。(由 Peixing Xin 在 bpo-41440 中贡献)

添加了一个新函数 os.eventfd() 和相关辅助函数,用于包装 Linux 上的 eventfd2 系统调用。(由 Christian Heimes 在 bpo-41001 中贡献)

添加 os.splice(),它允许在两个文件描述符之间移动数据,而无需在内核地址空间和用户地址空间之间复制,其中一个文件描述符必须引用一个管道。(由 Pablo Galindo 在 bpo-41625 中贡献)

为 macOS 添加了 O_EVTONLYO_FSYNCO_SYMLINKO_NOFOLLOW_ANY。(由 Donghee Na 在 bpo-43106 中贡献)

os.path

os.path.realpath() 现在接受一个仅限关键字的 `strict` 参数。当设置为 True 时,如果路径不存在或遇到符号链接循环,则会引发 OSError。(由 Barney Gale 在 bpo-43757 中贡献)

pathlib

PurePath.parents 添加了切片支持。(由 Joshua Cannon 在 bpo-35498 中贡献)

PurePath.parents 添加了负索引支持。(由 Yaroslav Pankovych 在 bpo-21041 中贡献)

添加了 Path.hardlink_to 方法,它取代了 link_to()。新方法具有与 symlink_to() 相同的参数顺序。(由 Barney Gale 在 bpo-39950 中贡献)

pathlib.Path.stat()chmod() 现在接受一个仅限关键字的 `follow_symlinks` 参数,以与 os 模块中的相应函数保持一致。(由 Barney Gale 在 bpo-39906 中贡献)

platform

添加 platform.freedesktop_os_release(),用于从 freedesktop.org os-release 标准文件检索操作系统标识。(由 Christian Heimes 在 bpo-28468 中贡献)

pprint

pprint.pprint() 现在接受一个新的 `underscore_numbers` 关键字参数。(由 sblondon 在 bpo-42914 中贡献)

pprint 现在可以漂亮地打印 dataclasses.dataclass 实例。(由 Lewis Gaul 在 bpo-43080 中贡献)

py_compile

py_compile 的命令行界面添加了 --quiet 选项。(由 Gregory Schevchenko 在 bpo-38731 中贡献)

pyclbr

pyclbr.readmodule()pyclbr.readmodule_ex() 返回的树中的 `Function` 和 `Class` 对象添加了 `end_lineno` 属性。它与现有的(开始)`lineno` 匹配。(由 Aviral Srivastava 在 bpo-38307 中贡献)

shelve

在创建 shelf 时,shelve 模块现在默认使用 pickle.DEFAULT_PROTOCOL 而不是 pickle 协议 3。(由 Zackery Spytz 在 bpo-34204 中贡献)

statistics

添加了 `covariance()`、Pearson 的 `correlation()` 和简单的 `linear_regression()` 函数。(由 Tymoteusz Wołodźko 在 bpo-38490 中贡献)

site

当模块未定义 *__loader__* 时,将回退到 *__spec__.loader*。(由 Brett Cannon 在 bpo-42133 中贡献。)

socket

异常 socket.timeout 现在是 TimeoutError 的别名。(由 Christian Heimes 在 bpo-42413 中贡献)

添加创建 MPTCP 套接字(使用 `IPPROTO_MPTCP`)的选项(由 Rui Cunha 在 bpo-43571 中贡献)

添加 `IP_RECVTOS` 选项以接收服务类型(ToS)或 DSCP/ECN 字段(由 Georg Sauthoff 在 bpo-44077 中贡献)

ssl

ssl 模块需要 OpenSSL 1.1.1 或更高版本。(由 Christian Heimes 在 PEP 644bpo-43669 中贡献)

ssl 模块对 OpenSSL 3.0.0 提供了初步支持,并增加了新选项 OP_IGNORE_UNEXPECTED_EOF。(由 Christian Heimes 在 bpo-38820bpo-43794bpo-43788bpo-43791bpo-43799bpo-43920bpo-43789bpo-43811 中贡献)

已弃用的函数和已弃用的常量的使用现在会导致 DeprecationWarningssl.SSLContext.options 默认设置了 OP_NO_SSLv2OP_NO_SSLv3,因此无法警告再次设置标志。 弃用部分 包含已弃用功能的列表。(由 Christian Heimes 在 bpo-43880 中贡献)

ssl 模块现在具有更安全/更强的默认设置。默认情况下禁用不具有前向保密性或 SHA-1 MAC 的密码。安全级别 2 禁止使用安全性低于 112 位的弱 RSA、DH 和 ECC 密钥。 SSLContext 默认使用最低协议版本 TLS 1.2。设置基于 Hynek Schlawack 的研究。(由 Christian Heimes 在 bpo-43998 中贡献)

已弃用的协议 SSL 3.0、TLS 1.0 和 TLS 1.1 不再受官方支持。Python 不会主动阻止它们。但是,OpenSSL 的构建选项、发行版配置、供应商补丁和密码套件可能会阻止成功的握手。

ssl.get_server_certificate() 函数添加了 `timeout` 参数。(由 Zackery Spytz 在 bpo-31870 中贡献)

ssl 模块使用堆类型和多阶段初始化。(由 Christian Heimes 在 bpo-42333 中贡献)

添加了一个新的验证标志 VERIFY_X509_PARTIAL_CHAIN。(由 l0x 在 bpo-40849 中贡献)

sqlite3

为 `connect()`、`enable_load_extension()` 和 `load_extension()` 添加了审计事件。(由 Erlend E. Aasland 在 bpo-43762 中贡献)

sys

添加了 `sys.orig_argv` 属性:传递给 Python 可执行文件的原始命令行参数列表。(由 Victor Stinner 在 bpo-23427 中贡献)

添加了 `sys.stdlib_module_names`,包含标准库模块名称的列表。(由 Victor Stinner 在 bpo-42955 中贡献)

_thread

`_thread.interrupt_main()` 现在接受一个可选的信号编号来模拟(默认值仍为 signal.SIGINT)。(由 Antoine Pitrou 在 bpo-43356 中贡献)

threading

添加了 `threading.gettrace()` 和 `threading.getprofile()`,用于检索由 `threading.settrace()` 和 `threading.setprofile()` 分别设置的函数。(由 Mario Corchero 在 bpo-42251 中贡献)

添加了 `threading.__excepthook__`,允许在 `threading.excepthook()` 被设置为损坏或不同的值时检索其原始值。(由 Mario Corchero 在 bpo-42308 中贡献)

traceback

`format_exception()`、`format_exception_only()` 和 `print_exception()` 函数现在可以将异常对象作为位置参数传递。(由 Zackery Spytz 和 Matthias Bussonnier 在 bpo-26389 中贡献)

types

重新引入了 `types.EllipsisType`、`types.NoneType` 和 `types.NotImplementedType` 类,提供了一套新的、可被类型检查器轻松解释的类型。(由 Bas van Beek 在 bpo-41810 中贡献)

typing

对于主要更改,请参阅 与类型提示相关的新功能

`typing.Literal` 的行为已更改,以符合 PEP 586,并匹配 PEP 中指定的静态类型检查器的行为。

  1. `Literal` 现在会去重参数。

  2. `Literal` 对象之间的相等比较现在与顺序无关。

  3. `Literal` 比较现在会尊重类型。例如,`Literal[0] == Literal[False]` 以前评估为 `True`。现在是 `False`。为了支持此更改,内部使用的类型缓存现在支持区分类型。

  4. `Literal` 对象在相等比较期间如果其任何参数不是 可哈希 的,现在将引发 `TypeError` 异常。请注意,使用不可哈希参数声明 `Literal` 不会引发错误。

    >>> from typing import Literal
    >>> Literal[{0}]
    >>> Literal[{0}] == Literal[{False}]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unhashable type: 'set'
    

(由 Yurii Karabas 在 bpo-42345 中贡献)

添加了新函数 `typing.is_typeddict()`,用于内省一个注解是否为 `typing.TypedDict`。(由 Patrick Reader 在 bpo-41792 中贡献)

仅声明了数据变量的 `typing.Protocol` 的子类,在与 `isinstance` 检查时将引发 `TypeError`,除非它们被 `@runtime_checkable()` 装饰。以前,这些检查会静默通过。如果用户想要运行时协议,他们应该用 `@runtime_checkable()` 装饰器装饰他们的子类。(由 Yurii Karabas 在 bpo-38908 中贡献)

从 `typing.io` 和 `typing.re` 子模块导入现在会发出 `DeprecationWarning`。这些子模块自 Python 3.8 起已弃用,将在未来版本的 Python 中移除。属于这些子模块的任何内容都应直接从 typing 导入。(由 Sebastian Rittau 在 bpo-38291 中贡献)

unittest

在 `unittest.TestCase` 中添加了新方法 `assertNoLogs()`,以补充现有的 `assertLogs()`。(由 Kit Yan Choi 在 bpo-39385 中贡献)

urllib.parse

Python 版本早于 Python 3.10 时,在 urllib.parse.parse_qs()urllib.parse.parse_qsl() 中允许使用 `;` 和 `&` 作为查询参数分隔符。出于安全考虑,并为了符合新的 W3C 建议,此行为已更改为仅允许单个分隔符键,默认为 `&`。此更改也影响 `cgi.parse()` 和 `cgi.parse_multipart()`,因为它们在内部使用了受影响的函数。有关更多详细信息,请参阅各自的文档。(由 Adam Goldschmidt、Senthil Kumaran 和 Ken Jin 在 bpo-42967 中贡献)

URL 部分中的换行符或制表符的存在可能导致某些形式的攻击。遵循 WHATWG 规范(更新了 RFC 3986),ASCII 换行符 `\n`、`\r` 和制表符 `\t` 字符将从 URL 中被解析器移除,以防止此类攻击。移除字符由新的模块级变量 `urllib.parse._UNSAFE_URL_BYTES_TO_REMOVE` 控制。(参见 gh-88048

xml

在 `xml.sax.handler` 模块中添加了 `LexicalHandler` 类。(由 Jonathan Gossage 和 Zackery Spytz 在 bpo-35018 中贡献)

zipimport

添加了与 PEP 451 相关的函数:`zipimport.zipimporter.find_spec()`、`zipimport.zipimporter.create_module()` 和 `zipimport.zipimporter.exec_module()`。(由 Brett Cannon 在 bpo-42131 中贡献)

添加了 `zipimport.zipimporter.invalidate_caches()` 方法。(由 Desmond Cheong 在 bpo-14678 中贡献)

优化

  • 构造函数 `str()`、`bytes()` 和 `bytearray()` 现在更快(对于小型对象来说约快 30-40%)。(由 Serhiy Storchaka 在 bpo-41334 中贡献)

  • `runpy` 模块现在导入的模块更少。`python3 -m module-name` 命令的启动时间平均快 1.4 倍。在 Linux 上,`python3 -I -m module-name` 在 Python 3.9 中导入 69 个模块,而在 Python 3.10 中仅导入 51 个模块(减少 18 个)。(由 Victor Stinner 在 bpo-41006bpo-41718 中贡献)

  • `LOAD_ATTR` 指令现在使用新的“每操作码缓存”机制。它现在对于常规属性来说速度提高了约 36%,对于 slot 来说速度提高了 44%。(由 Pablo Galindo 和 Yury Selivanov 在 bpo-42093 和 Guido van Rossum 在 bpo-42927 中贡献,基于最初在 PyPy 和 MicroPython 中实现的想法)

  • 使用 `--enable-optimizations` 构建 Python 时,现在会在编译和链接行中添加 `-fno-semantic-interposition`。这使用 `gcc` 构建使用 `--enable-shared` 的 Python 解释器速度提高了高达 30%。有关更多详细信息,请参阅 这篇文章。(由 Victor Stinner 和 Pablo Galindo 在 bpo-38980 中贡献)

  • 为 `bz2` / `lzma` / `zlib` 模块使用了新的输出缓冲区管理代码,并为 `_compression.DecompressReader` 类添加了 `.readall()` 函数。bz2 解压缩现在快 1.09 到 1.17 倍,lzma 解压缩快 1.20 到 1.32 倍,`GzipFile.read(-1)` 快 1.11 到 1.18 倍。(由 Ma Lin 贡献,Gregory P. Smith 审阅,在 bpo-41486

  • 在使用字符串化注解时,函数的注解字典不再在函数创建时创建。取而代之的是,它们被存储为字符串元组,然后在需要时由函数对象惰性地将其转换为注解字典。此优化将定义带注解函数的 CPU 时间减少了一半。(由 Yurii Karabas 和 Inada Naoki 在 bpo-42202 中贡献)

  • 子字符串搜索函数,如 `str1 in str2` 和 `str2.find(str1)`,现在有时会使用 Crochemore & Perrin 的“Two-Way”字符串搜索算法,以避免在长字符串上出现二次行为。(由 Dennis Sweeney 在 bpo-41972 中贡献)

  • 对 `_PyType_Lookup()` 添加了微优化,以在缓存命中等常见情况下提高类型属性查找性能。这使得解释器平均快 1.04 倍。(由 Dino Viehland 在 bpo-43452 中贡献)

  • 以下内置函数现在支持更快的 PEP 590 vectorcall 调用约定:`map()`、`filter()`、`reversed()`、`bool()` 和 `float()`。(由 Donghee Na 和 Jeroen Demeyer 在 bpo-43575bpo-43287bpo-41922bpo-41873bpo-41870 中贡献)

  • `BZ2File` 的性能通过移除内部 `RLock` 得到改善。这使得 `BZ2File` 在多线程同时读写时不像其在 `gzip` 和 `lzma` 中的等效类那样是线程安全的。 (由 Inada Naoki 在 bpo-43785 中贡献)

已弃用

  • 当前 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`、`is` 和 `or` 中的一个,则会发出弃用警告。在未来的版本中,它将变为语法警告,最后变为语法错误。(由 Serhiy Storchaka 在 bpo-43833 中贡献)

  • 从本版本开始,将开始清理旧的导入语义,这些语义是为了兼容 Python 2.7 而保留的。具体来说,`find_loader()`/`find_module()`(已被 `importlib.abc.MetaPathFinder.find_spec()` 取代)、`load_module()`(已被 `importlib.abc.Loader.exec_module()` 取代)、`module_repr()`(导入系统会为您处理)、`__package__` 属性(已被 `__spec__.parent` 取代)、`__loader__` 属性(已被 `__spec__.loader` 取代)和 `__cached__` 属性(已被 `__spec__.cached` 取代)将缓慢移除(以及 `importlib` 中的其他类和方法)。`ImportWarning` 和/或 `DeprecationWarning` 将适时发出,以帮助在过渡期间识别需要更新的代码。

  • 整个 `distutils` 命名空间已被弃用,将在 Python 3.12 中移除。有关更多信息,请参阅 模块更改 部分。

  • 向 `random.randrange()` 传递非整数参数已被弃用。`ValueError` 已被弃用,取而代之的是 `TypeError`。(由 Serhiy Storchaka 和 Raymond Hettinger 在 bpo-37319 中贡献)

  • 自 Python 3.6 起,`importlib` 的各种 `load_module()` 方法已被记录为弃用,但现在也会触发 `DeprecationWarning`。请使用 `exec_module()`。(由 Brett Cannon 在 bpo-26131 中贡献)

  • `zipimport.zipimporter.load_module()` 已弃用,优先使用 `exec_module()`。(由 Brett Cannon 在 bpo-26131 中贡献)

  • 导入系统对 `load_module()` 的使用现在会触发 `ImportWarning`,因为 `exec_module()` 更受欢迎。(由 Brett Cannon 在 bpo-26131 中贡献)

  • 导入系统对 `importlib.abc.MetaPathFinder.find_module()` 和 `importlib.abc.PathEntryFinder.find_module()` 的使用现在会触发 `ImportWarning`,因为分别更受欢迎的是 `importlib.abc.MetaPathFinder.find_spec()` 和 `importlib.abc.PathEntryFinder.find_spec()`。您可以使用 `importlib.util.spec_from_loader()` 来帮助迁移。(由 Brett Cannon 在 bpo-42134 中贡献)

  • 导入系统对 `importlib.abc.PathEntryFinder.find_loader()` 的使用现在会触发 `ImportWarning`,因为更受欢迎的是 `importlib.abc.PathEntryFinder.find_spec()`。您可以使用 `importlib.util.spec_from_loader()` 来帮助迁移。(由 Brett Cannon 在 bpo-43672 中贡献)

  • 各种 `importlib.abc.MetaPathFinder.find_module()` 的实现( `importlib.machinery.BuiltinImporter.find_module()`、`importlib.machinery.FrozenImporter.find_module()`、`importlib.machinery.WindowsRegistryFinder.find_module()`、`importlib.machinery.PathFinder.find_module()`、`importlib.abc.MetaPathFinder.find_module()`)、`importlib.abc.PathEntryFinder.find_module()`( `importlib.machinery.FileFinder.find_module()` )和 `importlib.abc.PathEntryFinder.find_loader()`( `importlib.machinery.FileFinder.find_loader()` )现在会引发 `DeprecationWarning`,并计划在 Python 3.12 中移除(以前它们在 Python 3.4 中被记录为弃用)。(由 Brett Cannon 在 bpo-42135 中贡献)

  • `importlib.abc.Finder` 已弃用(包括其唯一的 `find_module()` 方法)。`importlib.abc.MetaPathFinder` 和 `importlib.abc.PathEntryFinder` 不再继承自该类。用户应根据需要继承这两个类之一。(由 Brett Cannon 在 bpo-42135 中贡献)

  • `imp`、`importlib.find_loader()`、`importlib.util.set_package_wrapper()`、`importlib.util.set_loader_wrapper()`、`importlib.util.module_for_loader()`、`pkgutil.ImpImporter` 和 `pkgutil.ImpLoader` 的弃用已被更新为将 Python 3.12 列为计划移除版本(它们在以前的 Python 版本中就开始发出 `DeprecationWarning`)。(由 Brett Cannon 在 bpo-43720 中贡献)

  • 导入系统现在在使用 `__spec__` 属性之前,会回退到使用 `module_repr()` 来获取模块的 `__repr__()` 方法。`module_repr()` 的使用计划在 Python 3.12 中移除。(由 Brett Cannon 在 bpo-42137 中贡献)

  • `importlib.abc.Loader.module_repr()`、`importlib.machinery.FrozenLoader.module_repr()` 和 `importlib.machinery.BuiltinLoader.module_repr()` 已弃用,并计划在 Python 3.12 中移除。(由 Brett Cannon 在 bpo-42136 中贡献)

  • `sqlite3.OptimizedUnicode` 自 Python 3.3 起已不再被文档化且已过时,当时它被设为 `str` 的别名。现在它已被弃用,计划在 Python 3.12 中移除。(由 Erlend E. Aasland 在 bpo-42264 中贡献)

  • 未文档化的内置函数 `sqlite3.enable_shared_cache` 已被弃用,计划在 Python 3.12 中移除。SQLite3 文档强烈不建议使用它。有关更多详细信息,请参阅 SQLite3 文档。如果必须使用共享缓存,请使用 `cache=shared` 查询参数以 URI 模式打开数据库。(由 Erlend E. Aasland 在 bpo-24464 中贡献)

  • 以下 `threading` 方法现已弃用

    (由 Jelle Zijlstra 在 gh-87889 中贡献)

  • `pathlib.Path.link_to()` 已弃用,并计划在 Python 3.12 中移除。请使用 `pathlib.Path.hardlink_to()`。(由 Barney Gale 在 bpo-39950 中贡献)

  • `cgi.log()` 已弃用,并计划在 Python 3.12 中移除。(由 Inada Naoki 在 bpo-41139 中贡献)

  • 以下 `ssl` 功能自 Python 3.6、Python 3.7 或 OpenSSL 1.1.0 起已弃用,并将在 3.11 中移除

    • `OP_NO_SSLv2`、`OP_NO_SSLv3`、`OP_NO_TLSv1`、`OP_NO_TLSv1_1`、`OP_NO_TLSv1_2` 和 `OP_NO_TLSv1_3` 被 `ssl.SSLContext.minimum_version` 和 `ssl.SSLContext.maximum_version` 取代。

    • `PROTOCOL_SSLv2`、`PROTOCOL_SSLv3`、`PROTOCOL_SSLv23`、`PROTOCOL_TLSv1`、`PROTOCOL_TLSv1_1`、`PROTOCOL_TLSv1_2` 和 `PROTOCOL_TLS` 已弃用,优先使用 `ssl.PROTOCOL_TLS_CLIENT` 和 `ssl.PROTOCOL_TLS_SERVER`

    • `wrap_socket()` 被 `ssl.SSLContext.wrap_socket()` 取代

    • match_hostname()

    • `RAND_pseudo_bytes()`、`RAND_egd()`

    • NPN 功能,如 `ssl.SSLSocket.selected_npn_protocol()` 和 `ssl.SSLContext.set_npn_protocols()`,被 ALPN 取代。

  • 线程调试(`PYTHONTHREADDEBUG` 环境变量)已在 Python 3.10 中弃用,并将在 Python 3.12 中移除。此功能需要 Python 的 调试版本。(由 Victor Stinner 在 bpo-44584 中贡献)

  • typing.iotyping.re 子模块导入现在会发出 DeprecationWarning。这些子模块将在未来的 Python 版本中被移除。属于这些子模块的任何内容都应直接从 typing 导入。(由 Sebastian Rittau 在 bpo-38291 中贡献。)

已移除

  • 移除了 complex 类的特殊方法 __int__, __float__, __floordiv__, __mod__, __divmod__, __rfloordiv__, __rmod____rdivmod__。它们总是引发 TypeError。(由 Serhiy Storchaka 在 bpo-41974 中贡献。)

  • 私有且未文档化的 _markupbase 模块中的 ParserBase.error() 方法已被移除。html.parser.HTMLParserParserBase 的唯一子类,并且其 error() 实现已在 Python 3.5 中移除。(由 Berker Peksag 在 bpo-31844 中贡献。)

  • 移除了 unicodedata.ucnhash_CAPI 属性,它是一个内部 PyCapsule 对象。相关的私有 _PyUnicode_Name_CAPI 结构已移至内部 C API。(由 Victor Stinner 在 bpo-42157 中贡献。)

  • 移除了 parser 模块,该模块在 3.9 版本中因切换到新的 PEG 解析器而被弃用,以及所有仅供旧解析器使用的 C 源代码和头文件,包括 node.h, parser.h, graminit.hgrammar.h

  • 移除了在 3.9 版本中因切换到新的 PEG 解析器而被弃用的公共 C API 函数 PyParser_SimpleParseStringFlags, PyParser_SimpleParseStringFlagsFilename, PyParser_SimpleParseFileFlagsPyNode_Compile

  • 移除了 formatter 模块,该模块在 Python 3.4 中被弃用。它有些过时,使用很少,且未经测试。它原定于 Python 3.6 中移除,但此类移除推迟到 Python 2.7 EOL 之后。现有用户应将他们使用的类复制到他们的代码中。(由 Donghee Na 和 Terry J. Reedy 在 bpo-42299 中贡献。)

  • 移除了 PyModule_GetWarningsModule() 函数,由于 _warnings 模块在 2.6 中已转换为内置模块,因此该函数已无用。(由 Hai Shi 在 bpo-42599 中贡献。)

  • collections 模块中移除了到 Collections Abstract Base Classes 的已弃用别名。(由 Victor Stinner 在 bpo-37324 中贡献。)

  • 继 Python 3.8 中弃用之后,loop 参数已从 asyncio 的大部分 高层 API 中移除。此更改的动机是多方面的:

    1. 这简化了高层 API。

    2. 自 Python 3.7 起,高层 API 中的函数一直隐式获取当前线程运行的事件循环。在大多数正常用例中,无需将事件循环传递给 API。

    3. 事件循环传递容易出错,尤其是在处理不同线程中运行的循环时。

    请注意,低层 API 仍接受 loop。有关如何替换现有代码的示例,请参阅 Python API 的更改

    (由 Yurii Karabas, Andrew Svetlov, Yury Selivanov 和 Kyle Stanley 在 bpo-42392 中贡献。)

移植到 Python 3.10

本节列出了前面描述过的变更以及其他可能需要修改代码的 bug 修复。

Python 语法中的更改

  • 当编译之前有效的语法,并且数字字面量后面紧跟关键字时(例如 0in x),现在会发出弃用警告。在未来的版本中,它将被改为语法警告,最终成为语法错误。要消除警告并使代码与未来版本兼容,只需在数字字面量和后面的关键字之间添加一个空格即可。(由 Serhiy Storchaka 在 bpo-43833 中贡献。)

Python API 的变化

  • traceback 模块中的 format_exception()format_exception_only()print_exception() 函数的 etype 参数已重命名为 exc。(由 Zackery Spytz 和 Matthias Bussonnier 在 bpo-26389 中贡献。)

  • atexit:在 Python 退出时,如果使用 atexit.register() 注册的回调函数失败,其异常现在会被记录。之前,只有部分异常会被记录,最后一个异常总是被静默忽略。(由 Victor Stinner 在 bpo-42639 中贡献。)

  • collections.abc.Callable 泛型现在会展平类型参数,类似于 typing.Callable 的当前行为。这意味着 collections.abc.Callable[[int, str], str]__args__ 将是 (int, str, str);之前是 ([int, str], str)。通过 typing.get_args()__args__ 访问参数的代码需要考虑此更改。此外,对于参数化 collections.abc.Callable 的无效形式,可能会引发 TypeError,这些形式在 Python 3.9 中可能被静默通过。(由 Ken Jin 在 bpo-42195 中贡献。)

  • socket.htons()socket.ntohs() 现在如果给定的参数不适合 16 位无符号整数,则会引发 OverflowError 而不是 DeprecationWarning。(由 Erlend E. Aasland 在 bpo-42393 中贡献。)

  • 继 Python 3.8 中弃用之后,loop 参数已从 asyncio 的大部分 高层 API 中移除。

    一个协程,当前看起来像这样

    async def foo(loop):
        await asyncio.sleep(1, loop=loop)
    

    应该被替换为这样

    async def foo():
        await asyncio.sleep(1)
    

    如果 foo() 是专门设计为在当前线程的运行事件循环中运行(例如,在另一个线程的事件循环中运行),请考虑使用 asyncio.run_coroutine_threadsafe()

    (由 Yurii Karabas, Andrew Svetlov, Yury Selivanov 和 Kyle Stanley 在 bpo-42392 中贡献。)

  • types.FunctionType 构造函数现在会继承当前内置模块,如果 globals 字典没有 "__builtins__" 键,而不是使用 {"None": None} 作为内置模块:行为与 eval()exec() 函数相同。在 Python 中定义一个函数,如 def function(...): ...,不受影响,使用此语法无法覆盖全局变量:它也会继承当前的内置模块。(由 Victor Stinner 在 bpo-42990 中贡献。)

C API 的变化

  • C API 函数 PyParser_SimpleParseStringFlags, PyParser_SimpleParseStringFlagsFilename, PyParser_SimpleParseFileFlags, PyNode_Compile 以及这些函数使用的类型 struct _node,由于切换到新的 PEG 解析器而被移除。

    源代码现在应直接使用例如 Py_CompileString() 编译为代码对象。然后可以使用例如 PyEval_EvalCode() 来评估生成的代码对象。

    具体来说

    • 调用 PyParser_SimpleParseStringFlags 后跟 PyNode_Compile 可以通过调用 Py_CompileString() 来替换。

    • 没有直接替代 PyParser_SimpleParseFileFlags 的方法。要从 FILE * 参数编译代码,您需要用 C 读取文件并将结果缓冲区传递给 Py_CompileString()

    • 要使用 char * 文件名编译文件,请显式打开文件,读取它并编译结果。一种方法是使用 io 模块,并结合 PyImport_ImportModule(), PyObject_CallMethod(), PyBytes_AsString()Py_CompileString(),如下例所示。(声明和错误处理已省略。)

      io_module = Import_ImportModule("io");
      fileobject = PyObject_CallMethod(io_module, "open", "ss", filename, "rb");
      source_bytes_object = PyObject_CallMethod(fileobject, "read", "");
      result = PyObject_CallMethod(fileobject, "close", "");
      source_buf = PyBytes_AsString(source_bytes_object);
      code = Py_CompileString(source_buf, filename, Py_file_input);
      
    • 对于 FrameObject 对象,f_lasti 成员现在代表一个 wordcode 偏移量,而不是字节码字符串中的简单偏移量。这意味着需要将此数字乘以 2 才能用于期望字节偏移量的 API(例如,对于 PyCode_Addr2Line())。请注意,FrameObject 对象的 f_lasti 成员不被认为是稳定的:请改用 PyFrame_GetLineNumber()

CPython 字节码变更

  • MAKE_FUNCTION 指令现在接受字典或字符串元组作为函数的注解。(由 Yurii Karabas 和 Inada Naoki 在 bpo-42202 中贡献。)

构建更改

  • PEP 644:Python 现在要求 OpenSSL 1.1.1 或更高版本。不再支持 OpenSSL 1.0.2。(由 Christian Heimes 在 bpo-43669 中贡献。)

  • C99 函数 snprintf()vsnprintf() 现在是构建 Python 所必需的。(由 Victor Stinner 在 bpo-36020 中贡献。)

  • sqlite3 要求 SQLite 3.7.15 或更高版本。(由 Sergey Fedoseev 和 Erlend E. Aasland 在 bpo-40744bpo-40810 中贡献。)

  • atexit 模块现在必须始终构建为内置模块。(由 Victor Stinner 在 bpo-42639 中贡献。)

  • configure 脚本添加了 --disable-test-modules 选项:不构建或安装测试模块。(由 Xavier de Gaye, Thomas Petazzoni 和 Peixing Xin 在 bpo-27640 中贡献。)

  • ./configure 脚本添加了 --with-wheel-pkg-dir=PATH 选项。如果指定了该选项,ensurepip 模块将在此目录中查找 setuptoolspip wheel 包:如果两者都存在,则使用这些 wheel 包代替 ensurepip 捆绑的 wheel 包。

    一些 Linux 发行版的打包策略建议不要捆绑依赖项。例如,Fedora 将 wheel 包安装在 /usr/share/python-wheels/ 目录中,并且不安装 ensurepip._bundled 包。

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

  • 添加了一个新的 configure --without-static-libpython 选项,以不构建 libpythonMAJOR.MINOR.a 静态库,也不安装 python.o 对象文件。

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

  • 现在 configure 脚本使用 pkg-config 工具(如果可用)来检测 Tcl/Tk 头文件和库的位置。与之前一样,可以使用 --with-tcltk-includes--with-tcltk-libs 配置选项显式指定这些位置。(由 Manolis Stamatogiannakis 在 bpo-42603 中贡献。)

  • configure 脚本添加了 --with-openssl-rpath 选项。该选项简化了使用自定义 OpenSSL 安装构建 Python 的过程,例如 ./configure --with-openssl=/path/to/openssl --with-openssl-rpath=auto。(由 Christian Heimes 在 bpo-43466 中贡献。)

C API 更改

PEP 652:维护稳定 ABI

现在明确定义了扩展模块或嵌入 Python 的稳定 ABI(应用程序二进制接口)。C API 稳定性 描述了 C API 和 ABI 的稳定性保证以及使用稳定 ABI 的最佳实践。

(由 Petr Viktorin 在 PEP 652bpo-43795 中贡献。)

新功能

移植到 Python 3.10

  • 现在必须定义 PY_SSIZE_T_CLEAN 宏才能使用 PyArg_ParseTuple()Py_BuildValue() 格式,这些格式使用 #es#, et#, s#, u#, y#, z#, U#Z#。请参阅 解析参数和构建值PEP 353。(由 Victor Stinner 在 bpo-40943 中贡献。)

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

    #if PY_VERSION_HEX < 0x030900A4
    #  define Py_SET_REFCNT(obj, refcnt) ((Py_REFCNT(obj) = (refcnt)), (void)0)
    #endif
    

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

  • 出于历史原因,调用 PyDict_GetItem() 时不持有 GIL 是允许的。现在不再允许。(由 Victor Stinner 在 bpo-40839 中贡献。)

  • PyUnicode_FromUnicode(NULL, size)PyUnicode_FromStringAndSize(NULL, size) 现在会发出 DeprecationWarning。使用 PyUnicode_New() 来分配不带初始数据的 Unicode 对象。(由 Inada Naoki 在 bpo-36346 中贡献。)

  • PyCapsule API unicodedata.ucnhash_CAPI 的私有 _PyUnicode_Name_CAPI 结构已移至内部 C API。(由 Victor Stinner 在 bpo-42157 中贡献。)

  • Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix(), Py_GetProgramFullPath(), Py_GetPythonHome()Py_GetProgramName() 函数现在会在 Py_Initialize()(Python 初始化之前)调用之前返回 NULL。使用新的 Python 初始化配置 API 来获取 Python 路径配置。(由 Victor Stinner 在 bpo-42260 中贡献。)

  • PyList_SET_ITEM(), PyTuple_SET_ITEM()PyCell_SET() 宏现在不能用作左值或右值。例如,x = PyList_SET_ITEM(a, b, c)PyList_SET_ITEM(a, b, c) = x 现在会导致编译器错误。这可以防止 if (PyList_SET_ITEM (a, b, c) < 0) ... 这种测试中的错误。(由 Zackery Spytz 和 Victor Stinner 在 bpo-30459 中贡献。)

  • 非限制 API 文件 odictobject.h, parser_interface.h, picklebufobject.h, pyarena.h, pyctype.h, pydebug.h, pyfpe.hpytime.h 已移至 Include/cpython 目录。这些文件不应被直接包含,因为它们已经被包含在 Python.h 中;请参阅 包含文件。如果已被直接包含,请考虑改为包含 Python.h。(由 Nicholas Sim 在 bpo-35134 中贡献。)

  • 使用 Py_TPFLAGS_IMMUTABLETYPE 类型标志来创建不可变类型对象。不要依赖 Py_TPFLAGS_HEAPTYPE 来决定类型对象是否可变;而是检查是否设置了 Py_TPFLAGS_IMMUTABLETYPE。(由 Victor Stinner 和 Erlend E. Aasland 在 bpo-43908 中贡献。)

  • 未文档化的函数 Py_FrozenMain 已从有限 API 中移除。该函数主要用于自定义 Python 构建。(由 Petr Viktorin 在 bpo-26241 中贡献。)

已弃用

已移除

  • 移除了操作 Py_UNICODE* 字符串的 PyUnicode_str* 函数。(由 Inada Naoki 在 bpo-41123 中贡献。)

  • 移除了 PyUnicode_GetMax()。请迁移到新的(PEP 393)API。(由 Inada Naoki 在 bpo-41103 中贡献。)

  • 移除了 PyLong_FromUnicode()。请迁移到 PyLong_FromUnicodeObject()。(由 Inada Naoki 在 bpo-41103 中贡献。)

  • 移除了 PyUnicode_AsUnicodeCopy()。请使用 PyUnicode_AsUCS4Copy()PyUnicode_AsWideCharString() (由 Inada Naoki 在 bpo-41103 中贡献。)

  • 移除了 _Py_CheckRecursionLimit 变量:它已被 PyInterpreterState 结构中的 ceval.recursion_limit 替换。(由 Victor Stinner 在 bpo-41834 中贡献。)

  • 移除了未文档化的宏 Py_ALLOW_RECURSIONPy_END_ALLOW_RECURSION 以及 PyInterpreterState 结构中的 recursion_critical 字段。(由 Serhiy Storchaka 在 bpo-41936 中贡献。)

  • 移除了未文档化的函数 PyOS_InitInterrupts()。初始化 Python 已经隐式安装了信号处理程序:请参阅 PyConfig.install_signal_handlers。(由 Victor Stinner 在 bpo-41713 中贡献。)

  • 移除 PyAST_Validate() 函数。现在无法通过公共 C API 构建 AST 对象(mod_ty 类型)。该函数已经被排除在有限 C API 之外(PEP 384)。(由 Victor Stinner 在 bpo-43244 中贡献。)

  • 移除 symtable.h 头文件和未文档化的函数

    • PyST_GetScope()

    • PySymtable_Build()

    • PySymtable_BuildObject()

    • PySymtable_Free()

    • Py_SymtableString()

    • Py_SymtableStringObject()

    Py_SymtableString() 函数是稳定 ABI 的一部分,但它无法使用,因为 symtable.h 头文件被排除了有限 C API 之外。

    请改用 Python 的 symtable 模块。(由 Victor Stinner 在 bpo-43244 中贡献。)

  • 从有限 C API 头文件和提供 Windows 上稳定 ABI 的库 python3.dll 中移除了 PyOS_ReadlineFunctionPointer()。由于该函数接受 FILE* 参数,其 ABI 稳定性无法保证。(由 Petr Viktorin 在 bpo-43868 中贡献。)

  • 移除了 ast.h, asdl.hPython-ast.h 头文件。这些函数未文档化且被排除在有限 C API 之外。这些头文件定义的大多数名称都没有 Py 前缀,因此可能产生名称冲突。例如,Python-ast.h 定义了一个 Yield 宏,这与 Windows <winbase.h> 头文件使用的 Yield 名称冲突。请改用 Python 的 ast 模块。(由 Victor Stinner 在 bpo-43244 中贡献。)

  • 移除了使用 struct _mod 类型的编译器和解析器函数,因为公共 AST C API 已被移除

    • PyAST_Compile()

    • PyAST_CompileEx()

    • PyAST_CompileObject()

    • PyFuture_FromAST()

    • PyFuture_FromASTObject()

    • PyParser_ASTFromFile()

    • PyParser_ASTFromFileObject()

    • PyParser_ASTFromFilename()

    • PyParser_ASTFromString()

    • PyParser_ASTFromStringObject()

    这些函数未文档化且被排除在有限 C API 之外。(由 Victor Stinner 在 bpo-43244 中贡献。)

  • 移除 pyarena.h 头文件及其函数

    • PyArena_New()

    • PyArena_Free()

    • PyArena_Malloc()

    • PyArena_AddPyObject()

    这些函数未文档化,被排除了有限 C API 之外,并且仅由编译器内部使用。(由 Victor Stinner 在 bpo-43244 中贡献。)

  • 已移除 PyThreadState.use_tracing 成员以优化 Python。(由 Mark Shannon 在 bpo-43760 中贡献。)

3.10.7 中的安全特性

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

3.10.8 中的安全特性

已弃用的 mailcap 模块现在拒绝将不安全文本(文件名、MIME 类型、参数)注入到 shell 命令中。它会发出警告并假装未找到匹配项(或对于测试命令,假装测试失败),而不是使用此类文本。(由 Petr Viktorin 在 gh-98966 中贡献。)

3.10.12 中的重要更改

tarfile

  • tarfile 模块中的提取方法,以及 shutil.unpack_archive(),现在有一个新的 filter 参数,允许限制 tar 的一些可能令人惊讶或危险的功能,例如在目标目录之外创建文件。有关详细信息,请参阅 提取过滤器。在 Python 3.12 中,不带 filter 参数的用法将显示 DeprecationWarning。在 Python 3.14 中,默认值将切换到 'data'。(由 Petr Viktorin 在 PEP 706 中贡献。)