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: TypedDictLiteralFinalProtocol

Python 3.9 (EOL Oct 2025)

Typing: 内置泛型

现在可以使用 dict[...]list[...]set[...] 等,而不是使用 typing.Dict, List, Set

移除前缀/后缀

字符串和类似类型现在可以使用 removeprefixremovesuffix 更安全地移除开头或结尾的内容。 这比依赖于正确计算前缀长度(并在前缀发生变化时记住更改切片)的字符串切片方法更安全。

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 634PEP 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: slotskw_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  引入了一些语言特性,使程序能够同时引发和处理多个不相关的异常。 内置的 ExceptionGroupBaseExceptionGroup 类型可以将异常分组并一起引发,而新的 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

你也许感兴趣的:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注