Python 3.12正式发布了!
Python 3.12 正式版如今已经发布,带来了多项增强,其中包括f-string 解析改进
和Buffer Protocol(缓冲区协议)
等方面的优化。此次更新不仅改善了报错信息
,以便开发者更清晰地理解错误的根本原因,而且开发团队还进行了一系列性能优化,据称该版本的整体性能提升了约 5%。
主要变化
更灵活的 f-string 解析,允许许多以前不允许的事情 (PEP 701)
支持 Python 代码中的缓冲区协议(buffer 协议)(PEP 688)
引入新的 debugging/profiling API (PEP 669)
支持具有单独全局解释器锁的独立子解释器 (PEP 684)
优化性能,例如 PEP 709 和对 BOLT 二进制优化器的支持,预计总体性能提高 5%
改进错误信息,现在,可能由拼写错误引起的更多异常会向用户提出建议
类型注释:
- 为泛型类引入新的类型注释语法 (PEP 695)
- 为方法引入新的 override 装饰器 (PEP 698)
F-String的优化
新版取消了最初为f-strings
制定的一些限制,通过这些变化,使 f-strings 更加一致,成为可以直接整合到解析器中的正式化语法。这将为终端用户和库开发者带来显著的优势,同时也大幅降低解析f-strings
代码的维护成本。
f-string 中的表达式组件现在可以是任何有效的 Python 表达式,包括但不限于重用与 f 字符串相同引号的字符串、多行表达式、注释、反斜杠以及 Unicode 转义序列。
支持重用与f字符串相同引号的字符串
在之前的Python版本中,我们没法做这件事:
>>> haige = {"name": "haige"} >>> f"user: { haige["name"] }" File "", line 1 f"user: { haige["name"] }" ^^^^ SyntaxError: f-string: unmatched '[' >>> >>> f'user: { haige["name"] }' 'user: haige' >>> >>> >>> >>> f"user: { haige['name'] }" 'user: haige'
也就是说,在花括号里,我们无法使用f字符串
一样的单引号或是双引号。这实际上是一个颇具困扰的问题,意味着我们经常不得不在单引号和双引号之间切换。更为重要的是,这严重影响了我们使用嵌套 f-string 的能力。
在 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'
支持反斜杠以及 Unicode 转义序列
之前的Python版本是不支持在f-string中出现 的,比如:
>>> f"{'n'.join(['I', 'love', 'china'])}" File "", line 1 f"{'n'.join(['I', 'love', 'china'])}" ^ SyntaxError: f-string expression part cannot include a backslash
现在你可以这样定义表达式了:
>>> 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
支持多行表达式
在之前的Python版本中,f-string是必须要在同一行完成的。比如下面这个f-string,就是非法的:
s = f"{' '.join([ 'I', 'love', 'kobe' ])}"
现在这么做是完全没问题的了:
>>> 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'
支持任何有效的 Python 表达式
因此我们还可以调用函数并且进行操作了:
>>> def hello(): ... return "Hello World" ... >>> f"hell() return {":" + hello()}" 'hell() return :Hello World'
支持使用注释语法
3.12版本之前f-strings 中无法使用注释语法:
>>> f'''A complex trick: { ... bag['bag'] # recursive bags! ... }''' SyntaxError: f-string expression part cannot include '#'
支持任意嵌套 f 字符串
>>> f"{f"{f"{f"{f"{f"{[i for i in range(10)]}"}"}"}"}"}" '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'
更精确的错误消息提示
Python 3.12.0 (main, Oct 7 2023, 00:19:52) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> sys.version_info Traceback (most recent call last): File "", line 1, in NameError: name 'sys' is not defined. Did you forget to import 'sys'?
使用 TypedDict 进行更精确的**kwargs
键入
PEP 589引入了TypedDict
类型构造函数,支持由字符串键和可能不同类型的值组成的字典类型。
from typing import TypedDict, Unpack class Movie(TypedDict): name: str year: int def foo(**kwargs: Movie) -> None: ...
引用示例:
movie: dict[str, object] = {"name": "Life of Brian", "year": 1979} foo(**movie) # WRONG! Movie is of type dict[str, object] typed_movie: Movie = {"name": "The Meaning of Life", "year": 1983} foo(**typed_movie) # OK! another_movie = {"name": "Life of Brian", "year": 1979} foo(**another_movie) # Depends on the type checker.
覆盖静态类型的装饰器@override
Python的类型系统没有提供一种方法来识别需要更改的调用点,以保持一致性,当一个被重写的函数API发生变化时。这使得重构和转换代码更加危险。
from typing import override class Parent: def foo(self) -> int: return 1 def bar(self, x: str) -> str: return x class Child(Parent): @override def foo(self) -> int: return 2 @override def baz() -> int: # Type check error: no matching signature in ancestor return 1
新类型语法
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 695之前,定义一个泛型类
看起来像这样:
from typing import Generic, TypeVar _T_co = TypeVar("_T_co", covariant=True, bound=str) class ClassA(Generic[_T_co]): def method1(self) -> _T_co: ...
使用新语法,它看起来像这样:
class ClassA[T: str]: def method1(self) -> T: ...
另外,还引入了泛型别名定义的新方法:
type ListOrSet[T] = list[T] | set[T]
之前的泛型别名定义则是:
from typing import TypeAlias _T = TypeVar("_T") ListOrSet: TypeAlias = list[_T] | set[_T]
每个解释器一个 GIL
PEP-684 由“香农计划”的作者 Eric Snow 提出,主要是给每个子解释器创建 GIL,允许 Python 实现真正的并行处理。
说到并行处理,目前 Python 3.12 尚未引入「no-GIL 构建」。
按照计划,Python 团队会在 Python 3.13 中将 no-GIL 构建添加为实验性构建模式。
参考
docs.python.org/zh-tw/3.12/…
peps.python.org/pep-0695/
docs.python.org/zh-tw/3.12/…
peps.python.org/pep-0692/
peps.python.org/pep-0698/
docs.python.org/zh-tw/3.12/…