Python 版本之间的主要变化摘要
这篇文章的目的是快速参考 Python 每个新版本引入的主要变化。 这将有助于您在升级代码库时利用新特性的优势,或确保您拥有正确的保护程序以兼容旧版本。
本文章分为两部分:第一部分涉及实际变化,第二部分是有助于升级代码库的有用工具、链接和实用程序。
版本
在本节中,我记录了 Python 语法和标准库的主要变化。 除了类型模块,我主要排除了对模块的修改。 我没有包含对 C-API、字节码或其他低级部分的任何修改。
每个部分的生命周期结束日期 (EOL) 是指 Python 软件基金会不再为特定版本提供安全补丁的日期。
Python 3.7 及更早版本
本节已合并,因为在撰写本文时,所有这些版本都已过期,但如果您使用 Python 编程已有一段时间,可能已经忘记了这些功能是何时引入的。
- async 和 await (3.5+)
- matrix operator:
a @ b
(3.5+) - type hints (3.5+)
- 格式化字符串字面 (aka f-strings)
f"{something}"
(3.6+) - 数字字面中的下划线
1_000_000
(3.6+) - 字典保证有序插入 (3.7+)
contextvars
(3.7+)dataclasses
(3.7+)importlib.resources
(3.7+)
Python 3.8 (EOL Oct 2024)
赋值表达式
也称为 Walrus 运算符
if (thing := get_thing()) is not None:
do_something(thing)
else:
raise Exception(f"Something is wrong with {thing}")
仅位置参数
def foo(a, b, /, c, d, *, e, f):
# a, b: positional only
# c, d: positional or keyword
# e, f: keyword only
自说明 f-strings
# Before
f"user={user}"
# Now
f"{user=}"
Importlib Metadata
import importlib.metadata
importlib.metadata.version("some-library")
# "2.3.4"
importlib.metadata.requires("some-library")
# ["thing==1.2.4", "other>=5"]
importlib.metadata.files("some-library")
# [...]
Typing: TypedDict
, Literal
, Final
, Protocol
Python 3.9 (EOL Oct 2025)
Typing: 内置泛型
现在可以使用 dict[...]
, list[...]
, set[...]
等,而不是使用 typing.Dict, List, Set
。
移除前缀/后缀
字符串和类似类型现在可以使用 removeprefix
和 removesuffix
更安全地移除开头或结尾的内容。 这比依赖于正确计算前缀长度(并在前缀发生变化时记住更改切片)的字符串切片方法更安全。
if header.startswith("X-Forwarded-"):
section = header.removeprefix("X-Forwarded-")
Dict Union Operator (PEP 584)
combined_dict = dict_one | dict_two
updated_dict |= dict_three
Annotations (PEP 593)
my_int: Annotated[int, SomeRange(0, 255)] = 0
Zoneinfo (PEP 615)
IANA 时区数据库现已成为标准库的一部分
import zoneinfo
some_zone = zoneinfo.ZoneInfo("Europe/Berlin")
对于早期的 python 版本,可通过 PyPI 获取: backports.zoneinfo
.
Python 3.10 (EOL Oct 2026)
结构模式匹配 (PEP 634, PEP 635, PEP 636)
match command.split():
case ["quit"]:
print("Goodbye!")
quit_game()
case ["look"]:
current_room.describe()
case ["get", obj]:
character.get(obj, current_room)
case ["go", direction]:
current_room = current_room.neighbor(direction)
case [action]:
... # interpret single-verb action
case [action, obj]:
... # interpret action, obj
case _:
... # anything that didn't match
Typing: Union using pipe
# Before
from typing import Optional, Union
thing: Optional[Union[str, list[str]]] = None
# Now
thing: str | list[str] | None = None
Typing: ParamSpec
(PEP 612)
在使用 Callable
和其他类似类型时,可以更好地传递类型信息。
from typing import Awaitable, Callable, ParamSpec, TypeVar
P = ParamSpec("P")
R = TypeVar("R")
def add_logging(f: Callable[P, R]) -> Callable[P, Awaitable[R]]:
async def inner(*args: P.args, **kwargs: P.kwargs) -> R:
await log_to_database()
return f(*args, **kwargs)
return inner
@add_logging
def takes_int_str(x: int, y: str) -> int:
return x + 7
await takes_int_str(1, "A") # Accepted
await takes_int_str("B", 2) # Correctly rejected by the type checker
Typing: TypeAlias
(PEP 613)
StrCache: TypeAlias = 'Cache[str]' # a type alias
LOG_PREFIX = 'LOG[DEBUG]' # a module constant
Typing: TypeGuard
(PEP 647)
_T = TypeVar("_T")
def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeGuard[Tuple[_T, _T]]:
return len(val) == 2
def func(names: Tuple[str, ...]):
if is_two_element_tuple(names):
reveal_type(names) # Tuple[str, str]
else:
reveal_type(names) # Tuple[str, ...]
Parenthesized Context Managers (PEP 617)
with (CtxManager() as example):
...
with (
CtxManager1(), CtxManager2()
):
...
with (CtxManager1() as example, CtxManager2()):
...
with (CtxManager1(), CtxManager2() as example):
...
with (
CtxManager1() as example1,
CtxManager2() as example2,
):
...
Dataclasses: slots
, kw_only
Dataclass decorator 现在支持以下:
kw_only=True
__init__
中的所有参数都只标记关键字。slots=True
生成的 dataclass 将使用__slots__
来存储数据。
Python 3.11 (EOL Oct 2027)
Tomllib
tomllib
– Standard library TOML parser
Exception Groups (PEP 654)
PEP 654 引入了一些语言特性,使程序能够同时引发和处理多个不相关的异常。 内置的
ExceptionGroup
和BaseExceptionGroup
类型可以将异常分组并一起引发,而新的except*
语法则将except
概括为与异常组的子组匹配。
用注释丰富异常 (PEP 678)
在
BaseException
中添加了add_note()
方法。 该方法可用于为异常添加在异常发生时无法获得的上下文信息。 添加的注释会显示在默认回溯中。.
try:
do_something()
except BaseException as e:
e.add_note("this happened during do_something")
raise
Typing: Self
(PEP 673)
class MyClass:
@classmethod
def from_hex(cls, s: str) -> Self: # Self means instance of cls
return cls(int(s, 16))
def frobble(self, x: int) -> Self: # Self means this instance
self.y >> x
return self
Typing: LiteralString (PEP 675)
新的
LiteralString
注解可用于表示函数参数可以是任何字面字符串类型。 这就允许函数接受任意字面字符串类型,以及由其他字面字符串创建的字符串。 这样,类型检查程序就可以强制要求敏感函数(如执行 SQL 语句或 shell 命令的函数)只能使用静态参数调用,从而防止注入攻击。
Typing: Marking TypedDict
entries as [not] required (PEP 655)
# default is required
class Movie(TypedDict):
title: str
year: NotRequired[int]
# default is not-required
class Movie(TypedDict, total=False):
title: Required[str]
year: int
Typing: Variadic Generics via TypeVarTuple
(PEP 646)
PEP 484 以前引入了
TypeVar
,使创建参数化为单一类型的泛型成为可能。 PEP 646 增加了TypeVarTuple
,可使用任意数量的类型进行参数化。 换句话说,TypeVarTuple
是一个可变类型变量,可以实现可变泛型。
这样就可以实现多种使用情况。 特别是,它允许 NumPy 和 TensorFlow 等数值计算库中的类数组结构类型使用数组形状参数化。 静态类型检查程序现在可以捕捉到使用这些库的代码中与形状相关的错误。
Typing: @dataclass_transform
(PEP 681)
dataclass_transform
可用于装饰类、元类或本身就是装饰器的函数。 如果存在@dataclass_transform()
,静态类型检查程序就会知道被装饰的对象在运行时会对类进行 “魔法 “转换,使其具有类似dataclass
的行为。.
# The create_model decorator is defined by a library.
@typing.dataclass_transform()
def create_model(cls: Type[T]) -> Type[T]:
cls.__init__ = ...
cls.__eq__ = ...
cls.__ne__ = ...
return cls
# The create_model decorator can now be used to create new model classes:
@create_model
class CustomerModel:
id: int
name: str
for
语句中允许使用星形解包表达式:
这是官方支持的语法
for x in *a, *b:
print(x)
Python 3.12 (EOL Oct 2028)
Typing: 类型 参数语法 (PEP 695)
通用类和函数的紧凑注释
def max[T](args: Iterable[T]) -> T:
...
class list[T]:
def __getitem__(self, index: int, /) -> T:
...
def append(self, element: T) -> None:
...
使用type
语句(生成 TypeAliasType
)声明类型别名的功能
type Point = tuple[float, float]
# Type aliases can also be generic
type Point[T] = tuple[T, T]
F-string changes (PEP 701)
f-string 中的表达式组件现在可以是任何有效的 Python 表达式,包括与包含 f-string 的字符串重复使用相同引号的字符串、多行表达式、注释、反斜线和 unicode 转义序列。
可重复使用引号(包括嵌套 f 字符串语句
## Can re-use quotes
f"This is the playlist: {", ".join(songs)}"
f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}" # '2'
## Multiline f-string with comments
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
])}"
## Backslashes / Unicode
f"This is the playlist: {"\n".join(songs)}"
f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}"
Buffer protocol (PEP 688)
PEP 688 引入了一种在 Python 代码中使用buffer协议的方法。 实现了
__buffer__()
方法的类现在可以作为缓冲区类型使用。
新的
collections.abc.Buffer ABC
提供了表示缓冲区对象的标准方法,例如在类型注解中。 新的inspect.BufferFlags
枚举表示可用于自定义缓冲区创建的标志。
Typing: Unpack
for **kwargs
typing (PEP 692)
from typing import TypedDict, Unpack
class Movie(TypedDict):
name: str
year: int
def foo(**kwargs: Unpack[Movie]):
...
Typing: override
decorator (PEP 698)
确保子类重载的方法实际上存在于父类中。
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"
有用的东西
Postponed Annotations (PEP 563)
在 Python 的新版本中,类型注解在初始解析时被存储为字符串。 这有助于防止循环导入、需要在定义引用之前引用引用等问题。 从 3.7 开始的所有 Python 版本都支持 from __future__ import annotations
,这允许解释器使用这种新格式进行解析。
注:PEP 563 已被将在 Python 3.13 中实施的 PEP 649 所取代。
Typing Extensions
该库反向移植了键入功能,这样,检查旧代码库的键入检查程序就可以使用这些功能。
import sys
if sys.version_info < (3, 10):
from typing_extensions import TypeAlias
else:
from typing import TypeAlias
你也许感兴趣的:
- 【外评】Python 为何如此糟糕…
- 【外评】用 Python 解释 Rust 背后的思想或理念
- 【外评】Python 与苹果应用商店的拒绝作斗争
- 【外评】使用不安全的 Python 将速度提高 100 倍
- 谷歌裁掉整个 Python 团队!PyTorch 创始人急得直骂人:“WTF!核心语言团队无可替换”
- 谷歌Python团队全员被裁——负责内部Python所有基础设施、曾对数亿行代码执行自动重构
- 【译文】Python–一种深受喜爱但永远存在缺陷的语言
- 再同意不过了
- 【译文】减轻 Python 打包的痛苦
- 【译文】Python 打包,一年之后:回顾 2023 年的 Python 打包
你对本文的反应是: