Python 3.10 有什么新变化

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-12782 和 bpo-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 代码块不带 except 或 finally 代码块:
>>> 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() 来显示错误则此特性将没有效果,这可能发生在使用了某些其他自定义错误显示函数的时候。 这在某些 REPLs 例如 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. 针对 subject 在 match 语句中求值

  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 是一个 (x, y) 元组
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, 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。

可选的 EncodingWarning 和 encoding="locale" 选项

TextIOWrapper 和 open() 的默认编码格式取决于具体的平台和语言区域设置。 由于 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 来启用相应警告。

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

有关类型提示的新增特性

本节介绍了涉及 PEP 484 类型提示和 typing 模块的主要改变。

PEP 604: 新的类型联合运算符

引入了启用 X | Y 语法的类型联合运算符。 这提供了一种表示 ‘类型 X 或类型 Y’ 的相比使用 typing.Union 更清晰的方式,特别是在类型提示中。

在之前的 Python 版本中,要为可接受多种类型参数的函数应用类型提示,使用的是 typing.Union:

def square(number: Union[int, float]) -> Union[int, float]:
    return number ** 2

类型提示现在可以使用更简洁的写法:

def square(number: int | float) -> int | float:
    return number ** 2

这个新增语法也被接受作为 isinstance() 和 issubclass() 的第二个参数:

>>> isinstance(1, int | str)
True

详情参见 union 类型 和 PEP 604。

(由 Maggie Moss 和 Philippe Prados 在 bpo-41428 中贡献,并由 Yurii Karabas 和 Serhiy Storchaka 在 bpo-44490 中补充。)

PEP 612: 形参规格变量

在 typing 模块中新增了两个选项以改进用于 PEP 484 的 Callable 提供给静态类型检查器的信息。

第一个选项是形参规格变量。 它们被用来将一个可调用对象的形参类型转发给另一个可调用对象 —— 这种模式常见于高阶函数和装饰器。 使用示例可在 typing.ParamSpec 中找到。 在之前版本中,没有一种简单办法能以如此精确的方式对形参类型的依赖性进行类型标注。

第二个选项是新的 Concatenate 运算符。 它与形参规格变量一起使用以便对增加或移除了其他可调用对象的高阶可调用对象进行类型标注。 使用示例可以在 typing.Concatenate 中找到。

请参阅 typing.Callable, typing.ParamSpec, typing.Concatenate, typing.ParamSpecArgs, typing.ParamSpecKwargs 和 PEP 612 来了解更多细节。

(由 Ken Jin 在 bpo-41559 中贡献,并由 Jelle Zijlstra 在 bpo-43783 中加以少量改进。 PEP 由 Mark Mendoza 撰写。)

PEP 613: 类型别名

PEP 484 引入了类型别名的概念,只要求它们是不带标注的最高层级赋值。 这种简单性有时会使得类型检查器难以区分类型别名和普通赋值,特别是当涉及到前向引用或无效类型的时候。 例如在比较:

StrCache = 'Cache[str]'  # a type alias
LOG_PREFIX = 'LOG[DEBUG]'  # a module constant

现在 typing 模块具有一个特殊值 TypeAlias 可让你更明确地声明类型别名:

StrCache: TypeAlias = 'Cache[str]'  # a type alias
LOG_PREFIX = 'LOG[DEBUG]'  # a module constant

请参阅 PEP 613 了解详情。

(由 Mikhail Golubev 在 bpo-41923 中贡献。)

PEP 647: 用户自定义的类型保护器

TypeGuard 已被添加到 typing 模块用来标注类型保护函数并改进在类型细化期间提供给静态类型检查器的信息。 要了解更多信息,请参阅 TypeGuard 的文档以及 PEP 647。

(由 Ken Jin 和 Guido van Rossum 在 bpo-43766 中贡献。 PEP 由 Eric Traut 撰写。)

其他语言特性修改

  • 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 时禁止使用包含 yield 、 yield from 、 await 或已命名表达式的注解。(由 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_lineno 和 end_offset 属性。 如果不确定的话,它们将是 None 。(由 Pablo Galindo 在 bpo-43914 中贡献。)

新增模块

  • 无。

改进的模块

asyncio

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

argparse

在 argparse 的帮助中,将“可选参数”这一误导性短语改为“可选项”。某些测试代码如果依赖精确的输出匹配,可能需要调整。(由 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.unregister() 函数,用于取消对编解码器搜索函数的注册。(由 Hai Shi 在 bpo-41842 中贡献)

collections.abc

现在, parameterized generic 的 collections.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

name 和 birthday 都是生成的 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

在这里,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 命令已被移除。现在在 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 中增加了 encoding 和 errors 形参。 (由 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_dir 和 dir_fd 形参,用于指定搜索的根目录。(由 Serhiy Storchaka 在 bpo-38144 中贡献)

hashlib

hashlib 模块要求 OpenSSL 1.1.1 或更新版本。 (由 Christian Heimes 在 PEP 644 和 bpo-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 中贡献。)

在模式匹配语句中高亮显示新的 软关键字 match 、 case 和 _。 但这种高亮显示并不完美,某些极端情况下还会出现错误,包括 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 入口点 通过新增的 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

Add itertools.pairwise(). (Contributed by Raymond Hettinger in bpo-38200.)

linecache

若模块中没有定义 __loader__ ,则回退至使用 __spec__.loader 。(由 Brett Cannon 在 bpo-42133 中贡献)

os

Add os.cpu_count() support for VxWorks RTOS. (Contributed by Peixing Xin in bpo-41440.)

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

Add os.splice() that allows to move data between two file descriptors without copying between kernel address space and user address space, where one of the file descriptors must refer to a pipe. (Contributed by Pablo Galindo in bpo-41625.)

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

平台

Add platform.freedesktop_os_release() to retrieve operation system identification from freedesktop.org os-release standard file. (Contributed by Christian Heimes in 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

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

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