Python 3.12 有什么新变化

Python3.12发布着重于语言可用性提升、f-strings语法增强、类型形参和type语句的引入,同时改进了性能、安全性,包括GIL的解释器级管理、低开销监控、缓冲区协议和静态类型检查。部分旧特性被弃用,如distutils的移除和更精确的异常提示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘要 – 发布重点

Python 3.12 是 Python 编程语言的最新稳定发布版,包含一系列对语言和标准库的改变。 库的改变主要集中在清理已弃用的 API、可用性和正确性等方面。 值得注意的是,distutils 包已从标准库中移除。 os 和 pathlib 中的文件系统支持增加了许多改进,而且部分模块的性能也获得了提升。

语言的改变主要集中在可用性方面,如 f-字符串 的许多限制已被移除,而 ‘Did you mean …’ 提示消息继续得到改进。 新的 类型形参语法 和 type 语句提升了 泛型类型 和 类型别名 配合静态类型检查器使用时的效率。

本文并不试图提供所有新功能的完整规范说明,而是提供一个方便的概览。 如需了解完整细节,请参阅相应文档,如 标准库参考 和 语言参考。 如果你想了解某项改变的完整实现和设计理念,请参阅相应新特性的 PEP;但请注意一旦某项特性已完全实现则相应 PEP 通常不会再继续更新。

新的语法特性:

PEP 695,类型形参语法和 type 语句

新的语法特性:

PEP 701,f-字符串 语法的改进

解释器的改进:

PEP 684,解释器级的单独 GIL

PEP 669,低开销的监控

针对 NameError, ImportError 和 SyntaxError 异常 改进了 ‘Did you mean …’ 提示消息。

对 Python 数据模型的改进:

PEP 688,使用 Python 的 缓冲区协议

标准库中的重大改进:

pathlib.Path 类现在支持子类化

os 模块获得了多项针对 Windows 支持的改进

在 sqlite3 模块中添加了 命令行界面。

基于 运行时可检测协议 的 isinstance() 检测获得了 2 至 20 倍的提速

asyncio 包的性能获得了多项改进,一些基准测试显示有 75% 的提速。

在 uuid 模块中添加了 命令行界面。

由于 PEP 701 中的更改,通过 tokenize 模块生成令牌(token)的速度最多可提高 64%。

安全改进:

用来自 HACL* 项目的经过正式验证的代码替代 SHA1, SHA3, SHA2-384, SHA2-512 和 MD5 的内置 hashlib 实现。 这些内置实现保留作为仅在当 OpenSSL 未提供它们时使用的回退选项。

C API 的改进:

PEP 697,不稳定 C API 层

PEP 683,永生对象

CPython 实现的改进:

PEP 709,推导式内联化

对 Linux perf 性能分析器的 CPython 支持

在受支持的平台上实现栈溢出保护

新的类型标注特性:

PEP 692,使用 TypedDict 来标注 **kwargs

PEP 698,typing.override() 装饰器

重要的弃用、移除或限制:

PEP 623: 在 Python 的 C API 中移除 Unicode 对象中的 wstr,使每个 str 对象的大小缩减至少 8 个字节。

PEP 632: 移除 distutils 包。 请参阅 迁移指南 了解有关替换其所提供的 API 的建议。 第三方 Setuptools 包将继续提供 distutils,如果你在 Python 3.12 及更高版本中仍然需要它的话。

gh-95299: 不在使用 venv 创建的虚拟环境中预装 setuptools。 这意味着 distutils、setuptools、pkg_resources 和 easy_install 默认将不再可用;要访问这些工具请在 激活的 虚拟环境中运行 pip install setuptools。

移除了 asynchat、asyncore 和 imp 模块,以及一些 unittest.TestCase 方法别名。

新的特性

PEP 695: 类型形参语法

PEP 484 下的泛型类和函数是使用详细语法声明的,这使得类型参数的范围不明确,并且需要显式声明变化。

PEP 695 引入了一种新的、更紧凑、更明确的方式来创建 泛型类 和 函数:

def max[T](args: Iterable[T]) -> T:
    ...

class list[T]:
    def __getitem__(self, index: int, /) -> T:
        ...

    def append(self, element: T) -> None:
        ...

此外,该 PEP 引入了一种新的方法来使用 type 语句声明 类型别名,该语句会创建 TypeAliasType 的实例:

type Point = tuple[float, float]
类型别名也可以是 generic:

type Point[T] = tuple[T, T]
新语法允许声明 TypeVarTuple 和 ParamSpec 形参,以及带边界或约束的 TypeVar 形参:

type IntFunc[**P] = Callable[P, int]  # ParamSpec
type LabeledTuple[*Ts] = tuple[str, *Ts]  # TypeVarTuple
type HashableSequence[T: Hashable] = Sequence[T]  # TypeVar with bound
type IntOrStrSequence[T: (int, str)] = Sequence[T]  # TypeVar with constraints

类型别名的值以及通过此语法创建的类型变量的边界和约束仅在需要时才进行求值 (参见 惰性求值)。 这意味着类型别名可以引用稍后在文件中定义的其他类型。

通过类型参数列表声明的类型参数在声明的作用域和任何嵌套的作用域内都可见,但在外部作用域内不可见。 例如,它们可以用于泛型类的方法的类型注解或类体中。 但是,在定义类之后,不能在模块范围中使用它们。 有关类型参数的运行时语义的详细描述,请参见 类型形参列表。

为了支持这些作用域定义,引入了一种新的作用域,即 标注作用域。 标注作用域的行为在很大程度上类似于函数作用域,但与封闭类作用作用域的交互方式不同。 在 Python 3.13 中,标注 也将在标注作用域中进行求值。

更多细节请参见 PEP 695。

(PEP由 Eric Traut 撰写。 由 Jelle Zijlstra、Eric Traut 和其他人在 gh-103764 中实现。)

PEP 701:f-字符串的句法形式化

PEP 701 取消了对 f-字符串 使用的一些限制。 f-字符串内部的表达式部分现在可以是任何有效的 Python 表达式,包括重用了与标记 f-字符串本身相同的引号的字符串、多行表达式、注释、反斜杠以及 unicode 转义序列。 让我们详细介绍一下:

引号重用:在 Python 3.11 中,重用与标记 f-字符串本身相同的引号会引发 SyntaxError,迫使用户使用其他可用的引号(如在 f-字符串使用单引号时使用双引号或三重引号)。 在 Python 3.12 中,你现在可以这样做了:

>>>
songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
f"This is the playlist: {", ".join(songs)}"
'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'

请注意,在这一更改之前,对f-字符串的嵌套方式没有明确的限制,但字符串引号不能在f-字符串的表达式组件中重复使用,这使得不可能任意嵌套f-字符串。事实上,这是可以编写的嵌套最多的f-字符串:

>>>
f"""{f'''{f'{f"{1+1}"}'}'''}"""
'2'

由于现在f-字符串可以在表达式组件中包含任何有效的Python表达式,因此现在可以任意嵌套f-字符串:

>>>
f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"
'2'

多行表达式和注释:在 Python 3.11 中,f-字符串表达式必须在一行中完成定义,即使 f-字符串中的表达式在正常情况下可以跨多行(如在多行中定义的列表字面值),这使得它们更难被读懂。 在 Python 3.12 中,你现在可以定义跨越多行的 f-字符串并添加内联注释:

>>> f"This is the playlist: {", ".join([
...     'Take me back to Eden',  # My, my, those eyes like fire
...     'Alkaline',              # Not acid nor alkaline
...     'Ascensionism'           # Take to the broken skies at last
... ])}"
'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'

反斜杠和 unicode 字符:在 Python 3.12 之前,f-字符串表达式不能包含任何 \ 字符。 这也影响了 unicode 转义序列 (如 \N{snowman}),因为这些序列包含 \N 部分,而这部分以前不能作为 f-字符串表达式组件的一部分。 现在,你可以这样定义表达式:

>>>
print(f"This is the playlist: {"\n".join(songs)}")
This is the playlist: Take me back to Eden
Alkaline
Ascensionism
print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")


This is the playlist: Take me back to Eden♥Alkaline♥Ascensionism

更多细节请参见 PEP 701。

实现此特性的一个正面的附带影响是(通过使用 PEG 解析器 来解析 f-字符串),现在 f-字符串的错误消息会更加精确,包括错误的确切位置。例如,在 Python 3.11 中,下面的 f-字符串将引发一个 SyntaxError :

>>>
my_string = f"{x z y}" + f"{1 + 1}"
  File "<stdin>", line 1
    (x z y)
     ^^^
SyntaxError: f-string: invalid syntax. Perhaps you forgot a comma?

但是错误消息不包括错误在行中的确切位置,而且表达式被人为地用括号括起来。在Python 3.12中,由于f-字符串是用PEG解析器解析的,因此错误消息可以更精确,并显示整行:

>>>
my_string = f"{x z y}" + f"{1 + 1}"
  File "<stdin>", line 1
    my_string = f"{x z y}" + f"{1 + 1}"
                   ^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?

(由 Pablo Galindo、Batuhan Taskaya、 Lysandros Nikolaou、Cristián Maureira-Fredes 和 Marta Gómez 在 gh-102856 中贡献。 PEP 由 Pablo Galindo、 Batuhan Taskaya、 Lysandros Nikolaou 和 Marta Gómez 撰写)。

PEP 684: 解释器级 GIL

PEP 684 引入了解释器级的 GIL,使得现在可以创建带有独立的解释器级 GIL 的子解释器。 这将允许 Python 程序充分利用多个 CPU 核心。 此特性目前仅能通过 C-API 使用,不过相应的 Python API 预计将在 3.13 中添加。

使用新的 Py_NewInterpreterFromConfig() 函数来创建具有单独 GIL 的解释器:

PyInterpreterConfig config = {
    .check_multi_interp_extensions = 1,
    .gil = PyInterpreterConfig_OWN_GIL,
};
PyThreadState *tstate = NULL;
PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
if (PyStatus_Exception(status)) {
    return -1;
}
/* The new interpreter is now active in the current thread. */

有关如何将 C-API 用于子解释器和解释器级 GIL 的更多示例,请参见 Modules/_xxsubinterpretersmodule.c。

(由 Eric Snow 在 gh-104210 等中贡献。)

PEP 669:针对 CPython 的低影响监控

PEP 669 定义了一个新的 API 用于性能分析器、调试器和其他在 CPython 中监控事件的工具。 它覆盖了大范围的事件,包括调用、返回、行、异常、跳转等等。 这意味着你将只为你所使用的东西付出开销,提供了对近乎零开销的调试器和覆盖工具的支持。 请参阅 sys.monitoring 了解详情。

(由 Mark Shannon 在 gh-103082 中贡献。)

PEP 688: 使缓冲区协议在Python中可访问

PEP 688 引入了一种在 Python 代码中使用 缓冲区协议 的方法。实现 buffer() 方法的类现在可以作为缓冲区类型使用。

新的 collections.abc.Buffer ABC(抽象基类)提供了一种表示缓冲区对象的标准方法,例如在类型注释中。 新的 inspect.BufferFlags 枚举表示可用于自定义缓冲区创建的标志。 (由 Jelle Zijlstra 在 gh-102500 中贡献。)

PEP 709:推导式内联

字典、列表和集合推导式现在都是内联的,而不是为每次执行推导式都创建一个新的一次性函数对象。 这样可以将推导式的执行速度提高最多两倍。 更多细节请参阅 PEP 709。

推导式迭代变量将保持隔离而不会覆盖外作用域中的同名变量,在离开推导式后也不再可见。 内联确实会导致一些可见的行为变化:

回溯中的推导式不再有单独的帧,跟踪/评测也不再将推导式显示为函数调用。

symtable 模块将不再为每个推导式产生子符号表;取而代之的是,推导式的 locals 将包括在父函数的符号表中。

在推导式内部调用 locals() 现在包括该推导式外部外部的变量,而不再包括推导式“参数”导致的 .0 合成变量。

一个直接迭代 locals() 的推导式 (例如 [k for k in locals()]) 在启动追踪 (例如检测代码覆盖度) 的情况下运行时可能导致 “RuntimeError: dictionary changed size during iteration”。 此行为与现有的 for k in locals(): 等代码保持一致。 要避免此错误,可先创建一个由键组成的列表用于迭代: keys = list(locals()); [k for k in keys]。

(由 Carl Meyer 和 Vladimir Matveev 在 PEP 709 中贡献。)

改进的错误消息
当引发的 NameError 传播到最高层级时,解释器显示的错误消息可能将标准库中的模块作为建议的一部分。 (由 Pablo Galindo 在 gh-98254 中贡献。)

>>>
sys.version_info
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined. Did you forget to import 'sys'?

改进针对实例的 NameError 异常的错误建议。 现在如果在方法中引发了 NameError 而实例具有与异常中的名称完全相同的属性,建议将会包括 self. 而不是方法作用域中最接近的匹配项。 (由 Pablo Galindo 在 gh-99139 中贡献。)

>>>
class A:
   def __init__(self):
       self.blech = 1

   def foo(self):
       somethin = blech

A().foo()
Traceback (most recent call last):
  File "<stdin>", line 1
    somethin = blech
               ^^^^^
NameError: name 'blech' is not defined. Did you mean: 'self.blech'?

改进了当用户输入 import x from y 而不是 from y import x 时产生的 SyntaxError 错误消息。 (由 Pablo Galindo 在 gh-98931 中贡献。)

>>>
import a.y.z from b.y.z
Traceback (most recent call last):
  File "<stdin>", line 1
    import a.y.z from b.y.z
    ^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: Did you mean to use 'from ... import ...' instead?

由失败的 from import 语句引发的 ImportError 异常现在会包括根据 中的可用名称对 的值提出的建议。 (由 Pablo Galindo 在 gh-91058 中贡献。)

>>>
from collections import chainmap
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'chainmap' from 'collections'. Did you mean: 'ChainMap'?

有关类型提示的新增特性

本节介绍了影响 类型提示 和 typing 模块的主要更改。

PEP 692: 使用 TypedDict 进行更精确的 **kwargs 类型标注

在函数签名中的 **kwargs 类型标注(由 PEP 484 引入)只允许在所有 **kwargs 都属于同一类型的情况下进行有效标注。

PEP 692 通过依赖类型化的字典规定了一种更精确的针对 **kwargs 的类型标注方式:

from typing import TypedDict, Unpack

class Movie(TypedDict):
  name: str
  year: int

def foo(**kwargs: Unpack[Movie]): ...

更多细节请参见 PEP 692。

(由 Franek Magiera 在 gh-103629 中贡献。)

PEP 698:覆盖静态类型的装饰器

一个新的装饰器 typing.override() 已添加到 typing 模块中。 它向类型检查器指示该方法旨在重写超类中的方法。 这允许类型检查器在打算重写基类中的某个方法实际上没有重写的情况下捕获错误。

示例:

from typing import override

class Base:
  def get_color(self) -> str:
    return "blue"

class GoodChild(Base):
  @override  # ok: overrides Base.get_color
  def get_color(self) -> str:
    return "yellow"

class BadChild(Base):
  @override  # type checker error: does not override Base.get_color
  def get_colour(self) -> str:
    return "red"

更多细节参见 PEP 698。

(由 Steven Troxler 在 gh-101561 中贡献。)

Python 3.12 是该语言的一个重要版本,它于20233月正式发布。这个版本包含了一些特性和改进,虽然具体的细节可能会随着时间而更,以下是几个值得注意的部分: 1. **PEP 647 - Literal String Interpolation (f-string literals)**:引入了的字符串插值语法,类似于C++和JS中的模板字面量,使得字符串拼接更加简洁。 2. **Type Checking Improvements**:对类型注解的支持进一步增强,包括更严格的类型检查和更好的类型推断能力。 3. **Pattern Matching with `match` and `async match`**:类似正则表达式的模式匹配功能被添加到了`match`和`async match`函数中,用于处理可迭代对象的更复杂解析。 4. **Coroutine-friendly Generators**: 更好的协程支持,如`async for`循环现在可以正常中断并恢复执行,这对于异步迭代器处理非常有用。 5. **`typing.Protocol` Improvements**:协议类型(Protocol)的功能得到了加强,使得开发者可以更容易地定义自定义类型约束。 6. **`importlib.metadata` Enhancements**: 对模块元数据的访问进行了简化,方便获取软件包的信息。 7. **`sys.implementation`**: 提供了一个系统实施信息的属性,用于了解正在运行的Python实现的详细信息。 请注意,这只是一部分特性概述,实际使用中还有其他很多细节变化和性能提升。如果你打算升级Python 3.12,建议查看官方文档以获得最和详细的迁移指南。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值