[译] Python各版本的主要更改

简介: [译] Python各版本的主要更改

Python各版本的主要更改

译:

Summary of Major Changes Between Python Versions

https://www.nicholashairs.com/posts/major-changes-between-python-versions/

本文总结了Python 3.7到3.12的语法及标准库的主要更改,并且介绍了typing模块的主要更改。此外,还提到了各个版本的EOL(End of Life,Python官方不再提供安全补丁的日期)。

各个版本的主要更改

3.7及更早版本

python3.7版本在2023年7月 EOL。

3.5+:

  • 异步关键字:asyncawait
  • 矩阵运算符: a @ b
  • 类型提示(type hinting)

3.6+:

  • f字符串(f-ring)
  • 数字中下划线:1_000_000 (3.6+)

3.7+:

  • 字典按顺序插入元素
  • 上下文变量库contextvars
  • dataclasses
  • importlib.resources

3.8 (EOL 2024年10月)

  • 赋值表达式(walrus操作符) :=
if (thing := get_thing()) is not None:
    do_something(thing)
else:
    raise Exception(f"Something is wrong with {thing}")
  • 仅限位置参数(positional-only parameters)
def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)
  • 自记录字符串
# 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
  • TypedDict - PEP 589
  • Literal - PEP 586
  • Final - PEP 591
  • Protocol - PEP 544

3.9 (EOL 2025年10月)

  • Typing:内置泛型
    可以用
    dict[...], list[...], set[...]
    替代之前的
    typing.Dict, List, Set
  • 删除前缀/后缀字符串
    字符串(和类似类型)可用removeprefixremovesuffix 更安全地从开头或结尾删除内容。这比字符串切片更安全。
if header.startswith("X-Forwarded-"):
    section = header.removeprefix("X-Forwarded-")
  • 字典并运算符| (PEP 584)
combined_dict = dict_one | dict_two
updated_dict |= dict_three
  • annotation (PEP 593)
my_int: Annotated[int, SomeRange(0, 255)] = 0
  • Zoneinfo (PEP 615)
    IANA 时区数据库现在是标准库的一部分。
import zoneinfo
some_zone = zoneinfo.ZoneInfo("Europe/Berlin")

3.10 (EOL 2026年10月)

  • Pattern Matching(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:使用| 表示并
# 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, ...]

  • 带括号的上下文管理器(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
    数据类装饰器现在支持以下功能:
    kw_only=True__init__的所有参数都将标记为仅关键字
    slots=True:生成的数据类将用__slots__存储数据

3.11 (EOL 2027年10月)

  • tomllib:标准库TOML解析
  • Exception组(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 required or not (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 2028年10月)

  • Typing: Type Parameter Syntax (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 语句声明类型别名(generate TypeAliasType)

type Point = tuple[float, float]

# Type aliases can also be generic
type Point[T] = tuple[T, T]


  • F-string changes (PEP 701)
    f-字符串中的表达式组件现在可以是任何有效的Python表达式,包括重用与包含f-字符串相同引号的字符串,多行表达式,注释,反斜杠和Unicode转义序列。

可以重用引号(包括嵌套 f-string)

## 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"

Useful Things

  • Postponed Annotations (PEP 563)
    在较新版本的 Python 中,当类型注释最初解析时,它们被存储为字符串。这有助于防止循环导入、在定义之前需要引用以及其他许多问题。从 Python 3.7 开始支持此功能。

from __future__ import annotations

这允许解释器使用这种新格式进行解析。

注意:PEP 563 将被 PEP 649 取代,PEP 649 将在 Python 3.13 中实现。

Typing Extensions

typing_extensions库向后移植typing功能,以便它们可用于检查旧代码库的类型检查器。

import sys

if sys.version_info < (3, 10):
    from typing_extensions import TypeAlias
else:
    from typing import TypeAlias

查看Python各个版本的EOL:

https://endoflife.date/python

Ruff

https://github.com/astral-sh/ruff

Ruff是一个用 Rust 编写的 linter 和代码格式化程序。它变得非常流行,因为它的速度非常快。它还包括自动修复错误的能力。


可以将 ruff 与它的pyupgrade兼容 linter(UP)结合使用,通过ruff check --fix 自动升级代码库。


使用pyproject.toml时,ruff将遵循project.requires-python。

Pyupgrade

此工具可用于自动升级代码库。

https://github.com/asottile/pyupgrade

black

black是一个流行的代码格式化程序。

使用pyproject.toml时,black将遵循project.requires-python。

相关文章
|
18天前
|
Linux 数据库管理 Python
CentOS7编译安装Python3.10(含OpenSSL1.1.1安装),创建虚拟环境,运行Django项目(含sqlite版本报错)
CentOS7编译安装Python3.10(含OpenSSL1.1.1安装),创建虚拟环境,运行Django项目(含sqlite版本报错)
111 4
|
1月前
|
机器学习/深度学习 PyTorch 算法框架/工具
Python冷知识:如何找出新版本增加或删除了哪些标准库?
Python冷知识:如何找出新版本增加或删除了哪些标准库?
34 0
|
12天前
|
Linux iOS开发 MacOS
Python如何版本切换
【6月更文挑战第14天】Python如何版本切换
19 2
|
2天前
|
Python
如何查询Python包的所有历史版本
如何查询Python包的所有历史版本
13 5
|
7天前
|
Python
Python语言提供了多种输出格式化的方法,这些方法随着时间的推移和版本的更新而发展
【6月更文挑战第19天】Python格式化方法包括过时的`%`操作符,`str.format()`,推荐的f-string(Python 3.6+)和Template strings。f-string提供最佳的可读性和性能,`str.format()`是通用的,而`%`不推荐使用。模板字符串用于特定场景。对于旧版Python,使用`str.format()`或`%`。
16 4
|
12天前
|
Java Python
Python2.x 与 3​​.x 版本区别
Python2.x 与 3​​.x 版本区别
|
1月前
|
存储 文件存储 Python
Python基础 --- 实现学生管理系统(Python 文件版本)
Python基础 --- 实现学生管理系统(Python 文件版本)
43 4
|
1月前
|
关系型数据库 Java 分布式数据库
实时计算 Flink版操作报错合集之在使用 Python UDF 时遇到 requests 包的导入问题,提示 OpenSSL 版本不兼容如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
44 5
|
1月前
|
Python
Python 新版本有75个内置函数,你不会不知道吧(1)
Python 新版本有75个内置函数,你不会不知道吧(1)
Python 新版本有75个内置函数,你不会不知道吧(1)
|
1月前
|
API 开发者 Docker
python中版本不兼容问题
【5月更文挑战第3天】python中版本不兼容问题
83 2