Python 3.10 有什么新变化

编者:

Pablo Galindo Salgado

这篇文章介绍了 Python 3.10 相比 3.9 增加的新特性。 Python 3.10 发布于 2021 年 10 月 14 日。 更详细的信息可参阅 更新日志

摘要 -- 发布重点

新的语法特性:

  • 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

新的特性

带圆括号的上下文管理器

现在已支持使用外层圆括号来使多个上下文管理器可以连续多行地书写。 这允许将过长的上下文管理器集能够以与之前 import 语句类似的方式格式化为多行的形式。 例如,以下这些示例写法现在都是有效的:

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

更清楚的错误消息

SyntaxError

现在当解析包含有未关闭括号的代码时解释器会包括未关闭括号的位置而不是显示 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 中贡献。)

  • try 代码块不带 exceptfinally 代码块:

    >>> 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-字符串中使用 *

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

IndentationError

许多 IndentationError 异常现在具有更多上下文来提示是何种代码块需要缩进,包括语句的位置:

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

AttributeError

当打印 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 上是一种常见的情况。

NameError

当打印解释器所引发的 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 中是一种常见的情况。

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. 针对 subjectmatch 语句中求值

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

  4. 执行与被确认匹配的模式相关联的动作。

  5. 如果没有确认到一个完全的匹配,则如果提供了使用通配符 _ 的最后一个 case 语句,则它将被用作已匹配模式。 如果没有确认到一个完全的匹配并且不存在使用通配符的 case 语句,则整个 match 代码块不执行任何操作。

声明性方式

读者可能是通过 C, Java 或 JavaScript (以及其他许多语言) 中的 switch 语句将一个目标 (数据对象) 与一个字面值 (模式) 进行匹配的简单例子了解到模式匹配的概念的。 switch 语句常常被用来将一个对象/表达式与包含在 case 语句中的字面值进行比较。

更强大的模式匹配例子可以在 Scala 和 Elixir 等语言中找到。 这种结构化模式匹配方式是“声明性”的并且会显式地为所要匹配的数据指定条件(模式)。

虽然使用嵌套的“if”语句的“命令性”系列指令可以被用来完成类似结构化模式匹配的效果,但它没有“声明性”方式那样清晰。 相反地,“声明性”方式指定了一个匹配所要满足的条件,并且通过其显式的模式使之更为易读。 虽然结构化模式匹配可以采取将一个变量与一个 case 语句中的字面值进行比较的最简单形式来使用,但它对于 Python 的真正价值在于其针对目标类型和形状的处理操作。

简单模式:匹配一个字面值

让我们把这个例子看作是模式匹配的最简单形式:一个值,即主词,被匹配到几个字面值,即模式。在下面的例子中,status 是匹配语句的主词。模式是每个 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"

如果传给上述函数的 status 为 418,则会返回 "I'm a teapot"。 如果传给上述函数的 status 为 500,则带有 _ 的 case 语句将作为通配符匹配,并会返回 "Something's wrong with the internet"。 请注意最后一个代码块:变量名 _ 将作为 通配符 并确保目标将总是被匹配。 _ 的使用是可选的。

你可以使用 | (“ or ”)在一个模式中组合几个字面值:

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 语句中使用 _,可能会出现不存在匹配的情况。如果不存在匹配,则行为是一个 no-op。例如,如果传入了值为 500 的 status ,就会发生 no-op。

带有字面值和变量的模式

模式可以看起来像解包形式,而且模式可以用来绑定变量。在这个例子中,一个数据点可以被解包为它的 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) ,可以看作是上面所示字面值模式的扩展。接下来的两个模式结合了一个字面值和一个变量,而变量 绑定 了一个来自主词的值(point)。 第四种模式捕获了两个值,这使得它在概念上类似于解包赋值 (x, y) = point

模式和类

如果你使用类来结构化你的数据,你可以使用类的名字,后面跟一个类似构造函数的参数列表,作为一种模式。这种模式可以将类的属性捕捉到变量中:

class Point:
    x: int
    y: int

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")
带有位置参数的模式

你可以在某些为其属性提供了排序的内置类(例如 dataclass)中使用位置参数。 你也可以通过在你的类中设置 __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)。

约束项

我们可以向一个模式添加 if 子句,称为“约束项”。 如果约束项为假值,则 match 将继续尝试下一个 case 语句块。 请注意值的捕获发生在约束项被求值之前。:

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.")

其他关键特性

一些其他关键特性:

  • 类似于解包赋值,元组和列表模式具有完全相同的含义,而且实际上能匹配任意序列。 从技术上说,目标必须为一个序列。 因而,一个重要的例外是模式不能匹配迭代器。 而且,为了避免一个常见的错误,序列模式不能匹配字符串。

  • 序列模式支持通配符: [x, y, *rest](x, y, *rest) 的作用类似于解包赋值中的通配符。 在 * 之后的名称也可以为 _,因此 (x, y, *_) 可以匹配包含两个条目的序列而不必绑定其余的条目。

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

  • 子模式可使用 as 关键字来捕获:

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

    x1, y1, x2, y2 等绑定就如你在没有 as 子句的情况下所期望的,而 p2 会绑定目标的整个第二项。

  • 大多数字面值是按相等性比较的。 但是,单例对象 True, FalseNone 则是按标识号比较的。

  • 命名常量也可以在模式中使用。 这些命名常量必须为带点号的名称以防止常量被解读为捕获变量:

    from enum import Enum
    class Color(Enum):
        RED = 0
        GREEN = 1
        BLUE = 2
    
    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 为真值并使用了语言区域指定的默认编码格式时被发出。

增加了 -X warn_default_encoding 选项和 PYTHONWARNDEFAULTENCODING 来启用相应警告。

请参阅 文本编码格式 了解更多信息。

其他语言特性修改

  • int 类型新增了一个方法 int.bit_count(),返回给定整数的二进制展开中值为一的位数,或称“比特计量”。 (由 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 时禁止使用包含 yieldyield fromawait 或已命名表达式的注解。(由 Batuhan Taskaya 在 bpo-42725 中贡献)

  • 未绑定变量、super() 和其他可能改变符号表处理的表达式,现在在 from __future__ import annotations 时不能用作注解。(由 Batuhan Taskaya 在 bpo-42725 中贡献)

  • float 类型和 decimal.Decimal 类型的 NaN 值的哈希值现在取决于对象身份。以前,即便 NaN 值彼此不等,也都是哈希为 0。在创建包含多个 NaN 的字典和集合时,由于哈希冲突过度,导致了运行代价可能会二次方增长。(由 Raymond Hettinger 在 bpo-43475 中贡献)

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

  • SyntaxError 异常现在有 end_linenoend_offset 属性。 如果不确定的话,它们将是 None 。(由 Pablo Galindo 在 bpo-43914 中贡献。)

新增模块

  • 无。

改进的模块

asyncio

加入了缺失的 connect_accepted_socket() 方法。(由 Alex Grönholm 在 bpo-41332 中贡献)

argparse

在 argparse 的帮助中,将“可选参数”这一误导性短语改为“可选项”。某些测试代码如果依赖精确的输出匹配,可能需要调整。(由 Raymond Hettinger 在 bpo-9694 中贡献)

array

现在, array.arrayindex() 方法拥有可选的 startstop 参数。(由 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.unregister() 函数,用于取消对编解码器搜索函数的注册。(由 Hai Shi 在 bpo-41842 中贡献)

collections.abc

现在, parameterized genericcollections.abc.Callable__args__typing.Callable 一致了。 collections.abc.Callable generic 现在将类型参数扁平化了,类似于 typing.Callable 当前的做法。这意味着 collections.abc.Callable[[int, str], str] 将带有 (int, str, str) 的参数 __args__;以前是 ([int, str], str) 。为了做到这一变化, types.GenericAlias 现在可以被子类化,当对 collections.abc.Callable 类型进行下标访问时,将返回一个子类。注意,collections.abc.Callable 非法的参数化形式可能会触发 TypeError ,而在 Python 3.9 中可能就静默了。(由 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() 将指明下层的 ncurses 库是否提供了扩展颜色支持。(由 Jeffrey Kintscher 和 Hans Petter Jansson 在 bpo-36982 中贡献)

现在常量 BUTTON5_* 如果是由底层的 curses 库提供的,则会在 curses 模块中体现。(由 Zackery Spytz 在 bpo-39273 中贡献)

dataclasses

__slots__

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

仅限关键字字段

数据类现在支持在生成的 __init__ 方法中只用关键字的字段。 有许多方法可以指定只用关键字的字段。

你可以说每一个字段都是仅限关键字的:

from dataclasses import dataclass

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

namebirthday 都是生成的 __init__ 方法的仅限关键字的参数。

你可以在每个字段的基础上指定仅限关键字:

from dataclasses import dataclass, field

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

这里的只有 birthday 是仅限关键字。如果你在单个字段上设置 kw_only ,要注意由于仅限关键字的字段需要跟在非仅限关键字的字段后面,所以有关于重新排序的规则。 详情请看完整的数据类文件。

你也可以指定 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

在这里,zt 是仅限关键字形参,而 xy 不是。 (由 Eric V. Smith 在 bpo-43532 中贡献。)

distutils

整个 distutils 包已被废弃,将在 Python 3.12 中移除。其用指定包构建程序的功能已被第三方软件包 setuptoolspackaging 完全取代,而且大多数其他常用的 API 在标准库的其他地方都可以调用(如 platformshutilsubprocesssysconfig)。目前没有迁移 distutils 其他功能的计划,用到其他功能的应用程序应该准备好自己保留一份拷贝。请参考 PEP 632

在 Python 3.8 中废弃的 bdist_wininst 命令已被移除。现在在 Windows 中发布二进制包推荐采用 bdist_wheel 命令。(由 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。 作为模块常量提供的标准库枚举的 repr()module_name.member_name。 (由 Ethan Furman 在 bpo-40066 中贡献。)

增加 enum.StrEnum 用于所有成员均为字符串的枚举。 (由 Ethan Furman 在 bpo-41816 中贡献。)

fileinput

fileinput.input()fileinput.FileInput 中增加了 encodingerrors 形参。 (由 Inada Naoki 在 bpo-43712 中贡献。)

现在 fileinput.hook_compressed() 会在 mode 为 "r" 且文件被压缩时返回 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()iglob() 中增加了 root_dirdir_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 的回退版 pbkdf2_hmac() 已被弃用。 未来 PBKDF2-HMAC 将仅在 Python 带有 OpenSSL 编译时才可用。(由 Christian Heimes 在 bpo-43880 中贡献)

hmac

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

IDLE 与 idlelib

使 IDLE 调用 sys.excepthook() (当启动时没有 '-n' )。用户钩子以前是被忽略的。 (由 Ken Hilton 在 bpo-43008 中贡献。)

重新安排设置对话框。 将常规选项卡分成 Windows 和 Shell/Ed 选项卡。 将扩展帮助菜单的帮助源移至扩展标签。为新选项留出空间,并缩短对话框。后者使对话框更适合小屏幕。 (由 Terry Jan Reedy 贡献于 bpo-40468 。) 将缩进空间设置从字体标签移到新的 Windows 标签。 (由 Mark Roseman 和 Terry Jan Reedy 在 bpo-33962 中提供)。

上述变化已被反向移植到 3.9 维护版本中。

增加了 Shell 侧栏。 将主提示符(“>>>”)移至侧栏。二级提示符(“...”)也加入侧栏。在编辑器的行号侧栏上鼠标单击和可选的拖动,会选定一行或多行文本。在选定文本行后右击将显示包含“copy with prompts”的上下文菜单。这会将侧栏的提示符与选定文本行合并。该选项也会在文本的上下文菜单中显示。(由 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(history)的功能一致。

现在 importlib.metadata entry points 通过一个新的 importlib.metadata.EntryPoints 类,为由组和名称选择入口点提供了更好的体验。关于废弃之处和用法的更多信息,请参见文档中的兼容性说明。

添加了 importlib.metadata.packages_distributions(),用于将顶级 Python 模块和包解析为其 importlib.metadata.Distributions

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

为 VxWorks 实时操作系统加入 os.cpu_count() 的支持。(由 Peixing Xin 在 bpo-41440 中贡献)

加入一个新函数 os.eventfd() 及其助手函数,以封装 Linux 的系统调用 eventfd2 。(由 Christian Heimes 在 bpo-41001 中贡献)

加入 os.splice(),以便在两个文件描述符之间移动数据,而无需在内核地址空间和用户地址空间之间进行复制,其中一个文件描述符必须指向某个管道。(由 Pablo Galindo 在 bpo-41625 中贡献)

为 macOS 增加了 O_EVTONLY, O_FSYNC, O_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

平台

对从 freedesktop.org os-release 标准文件提取操作系统标识增加 platform.freedesktop_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() 返回的结果树中的 FunctionClass 对象上添加一个 end_lineno 属性。 它将匹配现有的 (起始) lineno。 (由 Aviral Srivastava 在 bpo-38307 中贡献。)

shelve

现在 shelve 在创建 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.timeoutTimeoutError 的别名。(由 Christian Heimes 在 bpo-42413 中贡献。)

加入用 IPPROTO_MPTCP 创建 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-38820, bpo-43794, bpo-43788, bpo-43791, bpo-43799, bpo-43920, bpo-43789bpo-43811 中贡献。)

现在,已弃用函数和使用已弃用常量会导致 DeprecationWarningssl.SSLContext.options 默认设置了 OP_NO_SSLv2OP_NO_SSLv3 ,因而设置此标记无法再次发出警告了。 弃用部分 列出了已弃用的特性。 (由 Christian Heimes 贡献于 bpo-43880

现在,ssl 模块默认设置的安全性提高了。默认情况下,不具备前向安全性或 SHA-1 MAC 的加密算法会被禁用。二级安全禁止安全性低于 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/handle()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

回溯

现在,format_exception()format_exception_only()print_exception() 函数可以接受一个异常对象,作为唯一的位置参数。(由 Zackery Spytz 和 Matthias Bussonnier 贡献于 bpo-26389

types

重新引入 types.EllipsisTypetypes.NoneTypetypes.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 对象的任何参数都不是 hashable ,在相等性比较时将引发 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.iotyping.re 子模块导入现在将发出 DeprecationWarning。 这些子模块从 Python 3.8 开始已被弃用并将在未来的某个 Python 版本中被移除。 任何属于这些子模块的东西都应当改为直接从 typing 导入。 (由 Sebastian Rittau 在 bpo-38291 中贡献。)

unittest

加入新方法 assertNoLogs() ,以补充现有的 assertLogs()。(由 Kit Yan Choi 贡献于 bpo-39385

urllib.parse

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 中存在换行符或制表符可能会导致某种形式的攻击。 根据更新了 RFC 3986 的 WHATWG 规范,urllib.parse 中的解析器将从 URL 中去除 ASCII 换行符 \n, \r 和制表符 \t 以防止这种攻击。 移除的字符将由一个新的模块层级变量 urllib.parse._UNSAFE_URL_BYTES_TO_REMOVE 来控制。 (参见 gh-88048。)

xml

xml.sax.handler 模块中加入一个 LexicalHandler 类。(由 Jonathan Gossage 和 Zackery Spytz 贡献于 bpo-35018

zipimport

加入 PEP 451 相关的方法: find_spec()zipimport.zipimporter.create_module()zipimport.zipimporter.exec_module()。(由 Brett Cannon 贡献于 bpo-42131

加入 invalidate_caches() 方法。(由 Desmond Cheong 贡献于 bpo-14678

性能优化

  • 现在,构造函数 str()bytes()bytearray() 速度更快了(小对象大约提速 30-40%)。(由 Serhiy Storchaka 贡献于 bpo-41334

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

  • 现在, LOAD_ATTR 指令会使用新的“单独操作码缓存”机制。对于常规属性大约会提速 36%,而对于槽位属性会加快 44%。(由 Pablo Galindo 和 Yury Selivanov 贡献于 bpo-42093 ),并由 Guido van Rossum 贡献于 bpo-42927,基于最初在 PyPy 和 MicroPython 中实现的思路。)

  • 现在,当用 --enable-optimizations 构建 Python 时,会在编译和链接命令行中添加 -fno-semantic-interposition。 这会让用带参数 --enable-sharedgcc 构建 Python 解释器时提速 30%。详情请参阅`这篇文章 <https://developers.redhat.com/blog/2020/06/25/red-hat-enterprise-linux-8-2-brings-faster-python-3-8-run-speeds/>`_ 。(由 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 str2str2.find(str1) ,有时会采用Crochemore & Perrin的“二路归并”字符串搜索算法,以避免长字符串的二次检索行为。(由 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-43575, bpo-43287, bpo-41922, bpo-41873bpo-41870 中贡献。)

  • BZ2File 的性能通过移除内部的 RLock 获得改善。 这使得 BZ2File 在面对多个同时的读取器和写入器不再是线程安全的,,就像 gziplzma 中的对应类一直以来的情况一样。 (由 Inada Naoki 在 bpo-43785 中贡献。)

弃用

移除

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

  • ParserBase.error() 方法(来自私有且未记入文档的 _markupbase 模块)已被移除。 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

  • 移除了公有 C API 函数 PyParser_SimpleParseStringFlags, PyParser_SimpleParseStringFlagsFilename, PyParser_SimpleParseFileFlagsPyNode_Compile,它们在 3.9 中由于切换到新的 PEG 解析器而被弃用。

  • 移除了 formatter 模块,它在 Python 3.4 中已被弃用。 它相当过时、极少被使用,并且未经测试。 它最初计划在 Python 3.6 中移除,但此移除被延迟到 Python 2.7 生命期结束之后。 现有用户应当将它们用到的所有类都拷贝到自己的代码中。 (由 Donghee Na 和 Terry J. Reedy 在 bpo-42299 中贡献。)

  • 移除了 PyModule_GetWarningsModule() 函数,现在被由于 _warnings 模块在 2.6 中被转换为内置模块而变得没有用处。 (由 Hai Shi 在 bpo-42599 中贡献。)

  • collections 模块中移除了已被弃用的 容器抽象基类 的别名。 (由 Victor Stinner 在 bpo-37324 中贡献。)

  • loop 形参已从大部分 asyncio高层级 API 中被移除,之前它们在 Python 3.8 中已被弃用。 这一改变的动机是多方面的:

    1. 这简化了高层级 API。

    2. 高层级 API 中的这些函数自 Python 3.7 起已经会隐式地获取当前线程正在运行的事件循环。 在大多数正常使用场景中都没有必要向 API 传入事件循环。

    3. 在处理不同线程中运行的事件循环时传递事件循环特别容易产生错误。

    请注意低层级 API 仍将接受 loop。 请参阅 Python API 的变化 来获取有关如何替换现有代码的示例。

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

移植到 Python 3.10

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

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

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

  • The loop parameter has been removed from most of asyncio's high-level API following deprecation in Python 3.8.

    现在如下协程:

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

  • 如果 globals 字典中没有 "__builtins__" 键,那么 types.FunctionType 构造器现在将继承当前值,而不是用 {"None": None},这与 eval()exec() 函数一致。 利用 def function(...): ... 定义一个 Python 函数则不受影响,globals 无法被这种语法覆盖:它也是继承了当前值。 (由 Victor Stinner 在 bpo-42990 中贡献。)

C API 的变化

  • 由于换成了新的 PEG 解析程序,C 语言 API 函数 PyParser_SimpleParseStringFlagsPyParser_SimpleParseStringFlagsFilenamePyParser_SimpleParseFileFlagsPyNode_Compile 以及这些函数用到的类型 struct _node 已被删除。

    现在应该用 Py_CompileString() 将源代码直接编译为代码对象。然后可以用 PyEval_EvalCode() 之类的东西来对其求值。

    特别地:

    • PyParser_SimpleParseStringFlagsPyNode_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 成员现在代表一个字代码偏移而不是相对字节码字符串的简单偏移。 这意味着此数字需要乘以 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

  • 编译 Python 现在需要用到 C99 函数 snprintf()vsnprintf() 。(由 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 选项:不编译也不安装 test 模块。(由 Xavier de Gaye、Thomas Petazzoni 和 Peixing Xin 贡献于 bpo-27640

  • ./configure 脚本中加入 --with-wheel-pkg-dir=PATH 选项。如果指定了该选项, ensurepip 模块会在该目录下查找 setuptoolspip 包:如果两者都存在,就会使用这些包,而不是surepip 绑定的包。

    某些 Linux 发行版的打包策略建议不要绑定依赖关系。比如 Fedora 在 /usr/share/python-wheels/ 目录下安装 wheel 包,而不安装 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

  • 私有结构体 _PyUnicode_Name_CAPI (PyCapsule API unicodedata.ucnhash_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 中贡献。)

弃用

  • 现在 PyUnicode_InternImmortal() 函数已被弃用,并将在 Python 3.12 中移除:请改用 PyUnicode_InternInPlace()。(由 Victor Stinner 贡献于 bpo-41692

移除

  • 移除了 Py_UNICODE_str* 函数,它被用于控制 Py_UNICODE* 字符串。 (由 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 初始化时已隐式安装了信号处理 handler:参见 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 中贡献。)

  • PyOS_ReadlineFunctionPointer() 已从受限 C API 头文件和 python3.dll 中移除,此 dll 为 Windows 中的稳定版 ABI 库。由于该函数可接受一个 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 中的重要安全特性

使用十进制以外的底,如 2(二进制)、4、8(八进制)、16(十六进制)、32 以外作为基数在 intstr 之间进行转换,如果字符串形式的数字数量超过一个限制,会抛出 ValueError,以避免因算法复杂而导致的潜在拒绝服务攻击。这是对 CVE-2020-10735 的缓解方案。这个限制可以通过环境变量、命令行旗标或 sys API 进行配置或禁用。参见 integer string conversion length limitation 文档。默认限制是字符串形式的 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 中贡献。)