本文介绍了 Python 3.11 与 3.10 相比的新功能。
(部分新功能)
Exception Groups
和 except *
Python 3.11 之前,解释器同一时间只能捕获一个异常。
对于需要同时捕获的无关异常、链式异常无能为力:
(一种情况)当 Python 标准库中的上下文管理器在 __exit__()
中引发异常时,会覆盖用户引发的异常。虽然用户异常以 __context__
的形式被保留,但是用户的 try...except...
无法捕获到原始异常。
# Exception
def foo():
try:
raise print(1 / 0)
except Exception as e:
print(e)
-----------------------
division by zero
# ExceptionGroup
def foo():
try:
raise print(1 / 0)
except ExceptionGroup as e:
print(e)
-----------------------
Traceback (most recent call last):
File "foo.py", line 26, in <module>
foo()
File "foo.py", line 20, in foo
print(1 / 0)
~~^~~
ZeroDivisionError: division by zero
from typing_extensions import Required, NotRequired, TypedDict
class Movie(TypedDict):
title: str
year: NotRequired[int]
# 等同上面定义内容
class Movie0(TypedDict, total=False):
title: Required[str]
year: int
for
语句中使用星号解包表达式现在 python3.9 和 python3.10 中也以支持。
a = [1, 2, 3]
b = [4, 5, 6]
for x in *a, *b:
print(x)
tomllib
import tomllib
with open("../pyproject.toml", "rb") as f:
data: dict = tomllib.load(f)
toml_str = """
python-version = "3.11.0"
python-implementation = "CPython"
"""
data0 = tomllib.loads(toml_str)
wsgiref.types
用于静态类型检查的WSGI类型。
math
math.exp2()
返回 2 的 x 次方
math.cbrt()
返回 x 的立方根
math.pow()
为了与 IEEE 754 规范保持一致,更改了两个极端情况。
math.pow(0.0, -math.inf) -> inf
math.pow(-0.0, -math.inf) -> inf
# 之前以上函数返回 -> ValueError
math.nan
math.nan
现在一直可用。
Python 将字节码缓存在 __pycache__
目录中以加速模块加载。
之前,在 3.10 中,
Read __pycache__ -> Unmarshal -> Heap allocated code object -> Evaluate
在 Python 3.11 中,Python 启动必不可少的核心模块被冻结。
这意味着,它们的代码对象(字节码)是由解释器静态分配的。
这将模块执行过程中的步骤减少为:
Statically allocated code object -> Evaluate
Python 3.11 中的解释器启动速度现在提高到了 10 - 15%。这对使用 Python 的短时间运行程序有很大的影响。
每当 Python 调用 Python 函数时,就会创建 Python 框架。该帧保存执行信息,以下是新的框架优化:
在 Python 函数调用期间,Python 将调用一个评估 C 函数来解释该函数的代码。这有效地将纯 Python 递归限制在对 C 堆栈安全的范围内。
在 3.11 中,当 CPython 检测到 Python 代码调用另一个 Python 函数时,它会建立一个新框架,并“跳转”到新框架内的新代码。这避免了完全调用 C 解释函数。
大多数 Python 函数调用现在不占用 C 堆栈空间。这加快了大多数此类调用的速度。
PEP 659是更快的 CPython 项目的关键部分之一。一般的想法是,虽然 Python 是一种动态语言,但大多数代码都有对象和类型很少更改的区域。这个概念被称为类型稳定性。
在运行时,Python 将尝试在执行代码中寻找通用模式和类型稳定性。然后 Python 将用更专业的操作替换当前操作。这种专门的操作使用仅适用于那些用例/类型的快速路径,这些用例/类型通常优于它们的通用对应物。这也引入了另一个称为内联缓存的概念,其中 Python 将昂贵操作的结果直接缓存在字节码中。
专门化程序还将某些公共指令对组合成一个超级指令。这减少了执行期间的开销。
Python 只有在看到“热”代码(多次执行)时才会专门化。这可以防止 Python 将时间浪费在运行一次的代码上。当代码过于动态或用途发生变化时,Python 也可以去专业化。定期尝试专业化,专业化尝试的成本也不会太高。这允许专业化以适应新的环境。