Python 3.11:新功能

2025年3月17日 | 阅读 12 分钟
Python 3.11: New Features

我们在这一篇文章中讨论了最有趣的 Python 3.11 新功能,有关更新和更改的完整列表,请参阅官方发行文档。

Python 3.11 于 2022 年 10 月 24 日发布。Python 的最新版本更快、更易于使用。它已经开发了十七个月,现在已准备好广泛使用。

每个 Python 版本都有增强和更改,3.11 也不例外。手册中列出了每一项。在此处发现最有趣和最有用的新功能。

您将在此课程中发现新功能和增强功能,例如:

  • 改进的错误消息,提供更详细的回溯
  • 任务和异常组使处理异步代码更加容易。
  • 众多新的类型功能增强了 Python 的静态类型支持。
  • 本地 TOML 支持,用于处理配置文件。
  • 通过 Faster CPython 项目的重大努力,代码执行速度更快。

如果您打算遵循本教程中的任何示例,请使用 Python 3.11。如何安装 Python 预发布版本以及 Python 3 安装与设置指南?在您的 PC 上安装新版本 Python 的几种替代方案。

您将了解更多关于即将到来的语言更新以及在更新到新版本之前需要考虑的事项。要获取展示 Python 3.11 新功能的代码示例,请单击下面的链接

更具信息量的错误回溯

Python 以其易于访问的语法和强大的数据结构,常常被认为是适合初学者的第一门编程语言。对于任何人来说,阅读 Python 遇到错误时显示的 Traceback 都可能很困难,特别是对于刚接触 Python 的人来说。

Python 的错误消息在 3.10 版本中得到了显著改进。Python 3.11 中最受期待的功能也将以类似的方式改善您的开发体验。回溯包含装饰性注释,可以加快错误消息的解释速度。

即将发布的版本包含哪些更改?

Prolog

我已经构建了两个 Docker 容器来比较 3.10 和 3.11 版本之间的差异。

第一个容器用于 3.10 版本

第二个容器用于 3.11 版本

当两个容器都运行时,我们可以使用 vs small code container 连接到它们。然后我们可以运行我的 Python 代码在每个环境中进行比较。

让我们首先在接下来的几节中演示一个代码示例,然后演示这两个版本之间如何不同。

1. 错误位置

3.10 版本中的输出

1
100
Traceback (most recent call last):
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 86, in _run_code
    exec(code, run_globalss)
  File "/root//py310//myapp/__main__.py", line 4, in <module>
    print(d["key_11"])
KeyError: 'key_11'

3.11 版本中的输出

1
100
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/root/py311/myapp/__main__.py", line 4, in <module>
    print(d["key_11"])
          ~^^^^^^^^^^
KeyError: 'key_11'

Python 3.11 向开发人员披露了改进的错误位置,带来了出色的开发体验。

2. 'self' 类型

'self' 类型之前已在类型扩展模块中引入,现在已提升为标准类型库。

上面的代码展示了一个目录的结构。该定义是递归的,因为目录包含子目录。

Python 3.10 的输出如下所示

Traceback (most recent call last):
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 86, in _run_code
    exec(code, run_globalss)
  File "/root//py310//myapp/__main__.py", line 2, in <module>
    from typing import List, Tuple, Self

Python 3.11 的输出如下所示

{'content': (['a.txt', 'b.txt'],
             [{'content': (['file1', 'file2'], None), 'name': 'dir1'}]),
 'name': 'dir2'}

注意:可以使用类名来注解子目录的性质。但是,如果父类的名称发生更改,则需要更新所有注解引用。

您可以像下面这样导入此代码,使其在 3.11 和 3.10 中都能正常工作

3. 异常注解

BaseException 类上的更新的 __note__ 类属性默认为 None。

您可以使用任何字符串覆盖它并添加更多信息。

Python 3.10 的输出如下所示

Traceback (most recent call last):
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 86, in _run_code
    exec(code, run_globalss)
  File "/root//py310//myapp/__main__.py", line 6, in <module>
    raise MyException("some exception")
__main__.MyException: some exception

Python 3.11 的输出如下所示

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/root/py311/myapp/__main__.py", line 6, in <module>
    raise MyException("some exception")
    ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 
MyException: some exception
this is my note :)

如您所见,由于添加了注解,开发人员现在可以更有效地传达异常,注解已添加到输出中。

4. 异常组

为了抛出一个异常集合并在 except 子句中进行管理,3.11 版本引入了一种新的异常类型 ExceptionGroup。

还引入了新的 except* 语法……

Python 3.10 的输出如下所示

File "/usr /local /lib/ python3.10 /rrunpy.py", line 189, in _run_module_as_main
    mod_name, mod_spec, code = _get_main_module_details(_Error)
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 223, in _get_main_module_details
    return _get_module_details(main_name)
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 157, in _get_module_details
    code = loader.get_code(mod_name)
  File "<frozen importlib._bootstrap_external>", line 1017, in get_code
  File "<frozen importlib._bootstrap_external>", line 947, in source_to_code
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/root//py310//myapp/__main__.py", line 18
    except * (ToYoungException, EmailIsInvalidException) as exception_group_1:

Python 3.11 的输出如下所示

validations failed
  + Exception Group Traceback (most recent call last):
  |   File "", line 198, in _run_module_as_main
  |   File "", line 88, in _run_code
  | ExceptionGroup:  (1 sub-exception)
  +-+ - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - -
    | Exception Group Traceback (most recent call last):
    |   File "/root/py311/myapp/__main__.py", line 14, in 
    |     raise ExceptionGroup(
    |     ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 
    | ExceptionGroup: Data validations (2 sub-exceptions)
    +-+---------------- 1 ----------------
      | ToYoungException: Age must be over 18 - age is 11
      +- - - - - - - - - - - - - - - - - - - - - - - - - - - - 
2 ----------------
      | EmailIsInvalidException: Email must be valid some_wannabe_email
      +- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File "/root/py311/myapp/__main__.py", line 20, in 
    |     raise ValueError from exception_group_1
    |     ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 
    | ValueError
    +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

正如我们所见,在讨论多个失败原因时,它确实很有用。

5. 嵌套异步推导式

Python 3.10 的输出如下所示

Traceback (most recent call last):
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 189, in _run_module_as_main
    mod_name, mod_spec, code = _get_main_module_details(_Error)
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 223, in _get_main_module_details
    return _get_module_details(main_name)
  File "/usr /local /lib/ python3.10 /rrunpy.py", line 157, in _get_module_details
    code = loader.get_code(mod_name)
  File "<frozen importlib._bootstrap_external>", line 1017, in get_code
  File "<frozen importlib._bootstrap_external>", line 947, in source_to_code
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/root//py310//myapp/__main__.py", line 11
    return { n: [x async for x in elements(n)] for n in range(3)}
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: asynchronous comprehension outside of an asynchronous function

Python 3.11 的输出如下所示

{0: [1], 1: [1, 1], 2: [1, 2, 4], 3: [1, 3, 9, 27], 4: [1, 4, 16, 64, 256]}

当代码进入推导式块时,它知道推导式的当前“函数上下文”。

如果推导式不是异步的,则不能在内部代码块中使用异步语句。

然而,在 Python 3.11 中,包含支持内部异步推导式的内部异步语句的推导式会自动变为异步。

TOML 解析器

与 JSON 和 CSV 类似,TOML 处理现在已包含在标准库中。

Python 3.10 的输出如下所示

Python 控制台

Python 3.11 的输出如下所示

{'clients': {'data': [['gamma', 'delta'], [1, 2]], 'hosts': ['alpha', 'omega']},
 'database': {'connection_max': 5000,
              'enabled': True,
              'ports': [8000, 8001, 8002],
              'server': '192.168.1.1'},
 'owner': {'dob': datetime.datetime(1979, 5, 27, 7, 32, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=57600))),
           'name': 'Tom Preston-Werner'},
 'servers': {'alpha': {'dc': 'eqdc10', 'ip': '10.0.0.1'},
             'beta': {'dc': 'eqdc10', 'ip': '10.0.0.2'}},
 'title': 'TOML Example'}

7. 性能增强

据说 Python 3.11 的运行速度比 Python 3.10 快 10-60%。此网站的基准测试区域对此有更多信息。

Python 3.11 即将发布,性能和开发人员体验都得到了改进。

8. 更快的源代码执行速度

Python 以其运行缓慢而闻名。例如,一个普通循环在 Python 中的运行速度比在 C 中慢几个数量级。代码执行速度通常不如程序员的生产力重要。有许多策略可以克服这一缺点。

此外,Python 在封装用更快的语言创建的库方面非常出色。例如,NumPy 计算比等效的纯 Python 计算快得多。因此,Python 成为数据科学领域的重要竞争者,并结合了编写代码的简便性。

基础 Python 语言仍在努力使其运行更快。Mark Shannon 在 2020 年秋季提出了一系列性能增强措施,可以添加到 Python 中。Shannon 计划是一个雄心勃勃的计划,旨在在多个版本中将 Python 的速度提高五倍。

Mark Shannon 和 Python 的发明者 Guido van Rossum 是 Faster CPython 项目的程序员之一,该项目得到了微软的支持和资助。基于 Faster CPython 项目,Python 3.11 进行了大量增强。您将在本节中了解更多关于专业自适应解释器的信息。此外,您将在后面的部分中发现更快的启动时间和零成本的异常。

PEP 659 描述了一个专业自适应解释器。基本目标是通过优化频繁执行的过程来加速运行代码。这与即时 (JIT) 编译类似。尽管如此,它对编译没有影响。相反,Python 的字节码会根据需要进行修改或更改。

在执行之前,Python 代码会被转换为字节码。由于字节码包含比传统 Python 代码更基础的指令,因此 Python 代码的每一行都会被分解成许多字节码语句。

您可以使用它来查看 Python 的字节码。以一个可以进行英尺和米之间转换的函数为例:

Python 控制台

每一行都包含一条字节码指令的信息。这五列是行号、字节地址、操作码名称、操作参数以及括号中包含的参数的解释。

要编写 Python,您可以学习字节码以外的内容。但是,它可以帮助您理解 Python 的内部工作原理。

字节码生成现在包含一个名为“加速”的新步骤。它会在运行时用自适应指令替换可以优化的指令。这些类中的每一个都会检查其使用方式,并可能据此进行定制。

当一个函数被调用一定次数后,加速就会开始。在 CPython 3.11 中,这发生在调用八次之后。您可以通过调用 dis() 并设置 adaptive 参数来观察解释器如何自适应字节码。首先,必须定义一个函数,然后用浮点数参数调用七次

Python 控制台

接下来,看看 feet to meters() 的字节码

目前您还看不到任何值得注意的东西。在此版本中仍然存在非自适应字节码。在第九次调用 feet to meters() 之后,这种情况会发生变化

一些原始指令已被更改为更具体的指令。BINARY OP 已被专门化为 BINARY OP MULTIPLY FLOAT,以更快地将两个浮点整数相乘。

即使 feet to meters() 函数在 feet 是浮点参数时被修改得更有效,它仍然可以通过回退到之前的字节码指令来正常处理其他参数类型。虽然内部过程已更改,但您的代码将继续正常运行。

具体指令继续自适应。您的函数再调用五十二次,但这次是使用整数参数

Python 控制台

Python 解释器仍然可以对这两个浮点数进行乘法运算。再调用一次 feet to meters() 并使用整数会导致它退出并恢复为通用、自适应指令

Python 控制台

在这种情况下,由于操作之一 0.3048 始终是浮点数,因此字节码指令被更改为 BINARY OP ADAPTIVE,而不是 BINARY OP MULTIPLY INT。

优化同类型整数之间的乘法比整数和浮点数相乘更困难。目前需要有专门用于乘法浮点数和整数的指令。

通过此示例,最好理解自适应专业解释器的工作原理。总的来说,您不必担心更改现有代码来从中受益。您的大部分代码将照常运行得更快。

在某些情况下,您可以通过重构代码来实现更有效的专门化。Brandt Bucher 的专业工具可以说明解释器如何处理您的代码。教程中提供了一个手动代码改进示例。收听 Speak Python to Me 播客以获取更多信息。

Faster CPython 项目的关键原则如下:

Python 不会因此项研究而发生任何根本性变化,并且大多数代码的整体运行速度都会更快。

根据测试,Python 3.11 通常比 Python 3.10 快 25%。(来源)。但是,您应该更关注它在您的代码上的表现,而不是 Python 3.11 在基准测试上的表现。要了解如何评估您的代码的有效性,请展开下面的框

检查您的代码性能。显示/隐藏

Faster CPython 项目仍在进行中,将于 2023 年 10 月发布的 Python 3.12 将包含多项优化。可以在 GitHub 上关注该项目。请参阅下面的对话和演示

  • EuroPython 2022:Mark Shannon 讲述我们如何让 Python 3.11 更快
  • Speak Python to Me:与 Guido 和 Mark 一起让 Python 更快
  • Brandt Bucher 的 Python Perf:专业、自适应解释器
  • Pablo Galindo Salgado 在 PyCon ES 的演讲“Faster CPython 项目:我们如何让 Python 3.11 更快”。

Faster CPython 是一个影响 Python 各个方面的重要项目。自适应专业解释器是该项目的一部分。您将在本教程的后续部分中了解另外两项优化:更快的启动和零成本异常。

学习这些升级,您将成为一名更好的程序员!