跳至主要内容

Python 3.11 有哪些新变化,值得您升级吗?

我们为您详细梳理即将登陆 Python 3.11 的最重要功能,包括如何安装测试版以及何时可以获取正式稳定版。
Bexruz (Bex) Tuychiev
Bexruz (Bex) Tuychiev

创建您的免费账户

实践并学习路径你的进步。

使用 Google 继续继续即表示您接受我们的 使用条款,我们的 隐私政策 ,并且您的数据存储在美国。

在过去四年里,Python 三次获得 TIOBE “年度语言” 奖项,四年中的三次都由它摘得。到 2022 年年中,这门广受欢迎的编程语言依然位居 TIOBE 指数榜首。

TIOBE Index

Python 位居 2022 年 6 月 TIOBE 指数榜首。

Python 的评分为 12.74%,意味着关键词“Python”出现在大约 13% 的所有编程相关搜索引擎查询中。换句话说,互联网上每 7 到 8 个编程问题中就有一个与 Python 有关。

如今,Python 经常与数据科学和人工智能领域的重大突破相伴。实践者看重它的简洁、覆盖多种任务的大量内置与第三方库,以及强大的社区。

随着 3.11 的最终稳定版将在 2022 年 10 月 3 日发布,Python 社区可以期待多项令人欢迎的更新,包括显著的速度提升、更好的错误处理与调试能力,以及对标准库的诸多易用性改进。

Python 3.11 有哪些新功能?

Python 3.11 更新日志几乎是无尽的修复、改进与新增条目清单,其中大多数您可能甚至不会注意到。不过,有几项关键的新功能可能会在稳定版发布后显著改善您的 Python 工作流。下面就来概述这些新特性。 

1. 速度提升

首先让数据科学家兴奋的重大变化是速度提升——标准基准测试套件相较 3.10 大约快了 25%。官方文档称,在某些情况下 3.11 最高可快 60%。下面介绍如何自己执行基准测试,以验证速度提升。 

要比较 Python 3.10 与 3.11 的速度,您需要安装 Docker。确认 Docker Desktop 正在运行后,在终端中执行以下两条命令,它们会为两个 Python 版本各自下载一个镜像。

$ docker run -d python:3.10.4-bullseye

$ docker run -d python:3.11-rc-bullseye

下载完成的镜像应可在您的 Docker 仪表盘中看到,您也可以在此启动它们。

Docker installation of python 3.11

启动容器后,您可以运行 docker ps 查看容器 ID、镜像、状态等详细信息。

运行中的容器也会显示在仪表盘的 Containers 选项卡下,您会在“Run/Stop”旁看到 CLI 按钮。为两个容器都点击该按钮,以启动分别附着在容器上的两个终端标签页。

在两个标签页中,都通过 pip 安装 pyperformance 包。然后在各自容器的终端中运行下面的命令。

针对 Python 3.10

$ pyperformance run -r -o py310.json

针对 Python 3.11

$ pyperformance run -r -o py310.jsone run -r -o py311.json

这些命令会对标准库中的约 60 个 Python 函数运行基准测试,并生成 JSON 元数据。我可选地添加了 -r 标签,告诉 pyperformance 我希望测试更为严格,这将更耗时。在我的案例中,两个容器运行在一台搭载 AMD Ryzen 12 核 CPU 的 Windows 机器上。

现在两个容器中各有一个 JSON 文件,我们需要把它们放到同一目录中以继续进行速度测试。为此,我们使用 docker cp 命令将 Python 3.11 容器中的 JSON 文件复制到 3.10 容器中。

在单独的终端标签页中运行以下命令,并根据您计算机上的实际路径进行更新:

$ docker cp crazy_snyder:/py311.json C:/Users/bex/Desktop

$ docker cp C:/Users/bex/Desktop/py311.json charming_proskuriakova:/

在我的例子中,crazy_snyder 和 charming_proskuriakova 是正在运行的容器 ID。现在两个 JSON 文件都在 Python 3.10 容器中。要完成基准测试并将结果保存为 CSV,请运行以下命令。

$ pyperformance compare py310.json py311.json --csv comparison.csv

$ docker cp charming_proskuriakova:/comparison.csv C:/Users/bex/Desktop/articles

最后,我们将 CSV 加载到 Pandas 中进行分析:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.read_csv("data/comparison.csv")
df.rename({"Base": "py310", "Changed": "py311", "Benchmark": "function"},
                   axis=1,
                   inplace=True)
>>> df.shape
(59, 3)
>>> df.sample(5)
 Functionpy310py311
40scimark_monte_carlo0.123920.08052
19mako0.019670.01289
1chameleon0.011310.00843
8float0.142650.09133
35regex_effbot0.003150.00261

数据框中的每一行都记录了两个版本运行给定 Python 函数所耗费的时间(毫秒)。我们来绘制速度提升的直方图。

df["speed_up"] = (df["py310"] / df["py311"]).round(3)

fig, ax = plt.subplots()

sns.histplot(df["speed_up"], bins=11)
ax.set(xlabel="Speedup (times)", 
           title="Python 3.10 vs. Python 3.11 - speedup comparison")

plt.show()

Difference in speed betweeon python 3.11 and python 3.10

从图中可以明显看出,Python 3.11 中大多数函数大约快了 1.2 倍(20%)。我们也可以用均值和中位数来验证这一估计。

>>> df["speed_up"].mean()
1.2605593220338982
>>> df["speed_up"].median()
1.207

看起来有一个案例的速度提升超过了 60%。 

>>> df["speed_up"].max()
1.899

同时也有四个案例显示 Python 3.10 比新版本更快。

>>>  (df["speed_up"] <= 1).sum() 
4

虽然纸面上的性能提升显而易见,但其具体影响会因人而异,而且 Python 3.11 的最终稳定版尚未发布。该基准测试使用的是纯 Python 函数,您在日常工作中可能并不会大量使用。不过,同时为 Python 3.10 与 3.11 搭建 Docker 环境,仍能帮助您评估针对自身用例的速度提升情况。 

2. 更好的错误消息

Python 3.11 另一项令人期待的特性是更精准的错误提示,它能指出错误的准确位置。相比返回长达百行、且末尾错误信息难以理解的 traceback,Python 3.11 会定位到导致错误的那个具体表达式。

Traceback errors in python 3.10

在上面的示例中,解释器指出由于值为 None 的 x 导致脚本崩溃。在当前版本的 Python 中,错误会比较模糊,因为有两个对象都带有“x”属性。而 3.11 的错误处理则清楚地指出了有问题的表达式。 

这个例子展示了 3.11 如何从深层嵌套的字典中定位错误,并清晰地标出出错的键属于哪一层。

Traceback messages in python 3.11

在复杂的算术表达式中,同样能看到这种精确度。

Traceback messages in Python 3.11

更新后的错误消息无疑是令人欢迎的补充,尤其能帮助在调试脚本上费力的数据科学家。

3. 异常备注(Exception Notes)

“清晰优于隐晦。”

上面这句话是Python 之禅的第二条准则,这 20 条设计原则之一强调 Python 代码应当尽可能表达清晰。

为强化这一设计理念,Python 3.11 引入了异常备注(PEP 678)。现在,在 except 子句中,您可以调用 add_note() 函数,在抛出错误时附加自定义消息。

import math

try:
  math.sqrt(-1)
except ValueError as e:
  e.add_note("Negative value passed! Please try again.")
  raise

Custom exceptions

如果您像下面这样编写了自定义异常类,也可以在受保护的类级属性 __notes__ 中为该类添加多条备注:

import math

class MyOwnError(Exception):
   # Should be a list of strings
   __notes__ = ["This is a custom error!"]

try:
   math.sqrt(-1)
except:
   raise MyOwnError

custom exceptions

现在,当解释器触发该错误时,会在常规消息体之后显示自定义消息。

此外,还引入了与新类 ExceptionGroup 搭配使用的 except* 子句语法。PEP 654 表示,在多种场景中可以用它同时抛出多个相互无关的错误。多数此类场景较为小众,例如处理 WebSocket、使用 asyncio 进行并发编程等,在日常数据科学工作中并不常见。如果您感兴趣,可以在此处了解新语法。

4. 新的类型特性:Self

静态类型有助于提升代码的可读性与可调试性。明确变量、函数输入与输出的具体类型,能帮您节省数小时的调试时间,也让他人更容易阅读您的代码。添加类型注解还能让现代 IDE 在您输入函数名时显示函数定义,便于他人理解您的函数。

此前,Python 中强大的 typing 模块几乎为所有数据类型提供了类,唯独缺少“返回自身实例”的类。在当前版本的 Python 中,下面的示例是无法实现的:

from typing import Self
class Language:

  def __init__(self, name, version, release_date):
      self.name = name
      self.version = version
      self.release_date = release_date

  def change_version(self, version) -> Self:
      self.version = version

      return Language(self.name, self.version, self.release_date)

而在 Python 3.11 中,引入了 Self 类。如果函数的返回值是 self 或该类的新实例,您就可以在函数定义中使用 Self。

5. 标准库的改进

标准库还有一些其他的易用性改进。首先,math 模块新增了两个期待已久的函数。

>>> import math
>>> math.cbrt(9)  # Find the cube-root of x
2.080083823051904
>>> math.cbrt(27)
3.0000000000000004
>>> math.exp2(5)  # Raise 2 to the power of x
32.0

立方根函数竟然让 Python 等了 28 年才加入,确实令人惊讶,但俗话说得好,“亡羊补牢,犹未为晚”。  

fractions 模块也新增了一个功能,允许您从字符串创建分数:

>>> from fractions import Fraction
>>> Fraction("22/7")  # New in Python 3.11
Fraction(22, 7)
>>> Fraction(numerator=22, denominator=7)
Fraction(22, 7)
>>> Fraction("3e-4")
Fraction(3, 10000)
>>> Fraction("-73/41")
Fraction(-73, 41)
>>> Fraction(3.1415)  # Find the closest approximation to the given float
Fraction(7074029114692207, 2251799813685248)

如您所见,fractions 模块在某些算术运算中相当实用。我尤其喜欢它能找到最接近给定浮点数的分数。您还可以进一步通过设置分母上限来控制:

>>> from math import pi
>>> Fraction(pi).limit_denominator(100000)
Fraction(312689, 99532)

此外,还新增了用于解析 TOML 文档的 tomllib 模块。TOML(Tom's Obvious Minimal Language)是一种用于编写人类可读配置文件的流行格式。下面这张图来自项目主页:

TOML landing page

import tomllib

toml_str = """
python-version = "3.11.0"
release_date = "2022-10-22"
"""

data = tomllib.loads(toml_str)
print(data)  # {'python-version': '3.11.0', 'release_date': '2022-10-22'}
print(data['python-version'])  # 3.11.0
print(data['release_date'])  # 2022-10-22

Python 3.11 的发布时间

Python 3.11 的开发始于 2021 年 5 月 5 日。此后发布了七个 alpha 版本,并于 2022 年 5 月 8 日首次推出 beta 版。从该日期起直到正式发布,Python 将不再引入新功能。

最终的稳定版将于 2022 年 10 月 3 日发布,其间还会有三个 beta 版与两个候选版本。想要进一步了解 Python,您可以查看以下资源。

主题