Python 各版本主要变化速览

这篇文章旨在快速介绍Python每个新版本所带来的主要变化。这有助于你在升级代码库时利用新特性,或者确保你有适当的防护措施以兼容旧版本。

这篇内容分为两个部分:第一部分介绍实际的变化,第二部分介绍有助于升级代码库的实用工具、链接和程序。

版本

在本节中,我记录了Python语法和标准库的主要变化。除了typing模块外,我基本排除了其他模块的变化。我没有包含任何关于C-API、字节码或其他底层部分的变化。

对于每个部分,终止支持日期(EOL)指的是Python软件基金会将不再为特定版本提供安全补丁的日期。

Python 3.7及更早版本(2023年6月停止支持)

本节已合并,因为在撰写本文时,所有这些版本都已停止支持,但如果你已经用Python编程有一段时间了,可能已经忘记这些功能是何时引入的了。

  • async 和 await(3.5+)
  • 矩阵运算符:a @ b(3.5+)
  • 类型提示(3.5+)
  • 格式化字符串字面值(又名f-字符串)f"{something}"(3.6及以上版本)
  • 数字字面量中的下划线 1_000_000(3.6+)
  • 字典保证插入顺序(3.7+)
  • contextvars(3.7+)
  • dataclasses(3.7+)
  • importlib.resources(3.7+)
  • from __future__ import annotations(3.7+,在3.14中被取代)

Python 3.8(生命周期结束 2024年10月)

  1. 赋值表达式
    也被称为海象运算符

    python 复制代码
    if (thing := get_thing()) is not None:
      do_something(thing)
    else:
      raise Exception(f"Something is wrong with {thing}")
  2. 仅位置参数

    python 复制代码
    def foo(a, b, /, c, d, *, e, f):
      # a, b: positional only
      # c, d: positional or keyword
      # e, f: keyword only
  3. 自文档化f-字符串

    python 复制代码
    # Before
    f"user={user}"
    
    # Now
    f"{user=}"
  4. 导入库元数据

    python 复制代码
    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")
    # [...]
  5. 类型标注 :TypedDict、Literal、Final、Protocol

    • TypedDict - PEP 589
    • Literal - PEP 586
    • Final - PEP 591
    • Protocol - PEP 544

Python 3.9(终止支持 2025年10月)

  1. 类型标注:内置泛型
    现在可以使用dict[...]list[...]set[...]等,而不是使用typing.Dict, List, Set

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

    python 复制代码
    section = header.removeprefix("X-Forwarded-")
  3. 字典联合运算符(PEP 584)

    python 复制代码
    combined_dict = dict_one | dict_two
    updated_dict |= dict_three
  4. 注解(PEP 593)

    python 复制代码
    my_int: Annotated[int, SomeRange(0, 255)] = 0
  5. Zoneinfo(PEP 615)
    IANA时区数据库现已成为标准库的一部分

    python 复制代码
    import zoneinfo
    some_zone = zoneinfo.ZoneInfo("Europe/Berlin")

    对于早期的Python版本,可以通过PyPI获取:backports.zoneinfo

Python 3.10(终止支持 2026年10月)

  1. 结构化模式匹配(PEP 634、PEP 635、PEP 636)
    参见变更日志以获取更多示例。

    python 复制代码
    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
  2. 类型标注:使用竖线的联合类型

    python 复制代码
    # Before
    from typing import Optional, Union
    thing: Optional[Union[str, list[str]]] = None
    
    # Now
    thing: str | list[str] | None = None
  3. 类型标注:ParamSpec(PEP 612)
    在处理Callable和其他类似类型时,能够更好地传递类型信息。

    python 复制代码
    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
  4. 类型标注:TypeAlias(PEP 613)

    python 复制代码
    StrCache: TypeAlias = 'Cache[str]'  # a type alias
    LOG_PREFIX = 'LOG[DEBUG]'  # a module constant
  5. 类型标注:TypeGuard(PEP 647)

    python 复制代码
    _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, ...]
  6. 带括号的上下文管理器(PEP 617)

    python 复制代码
    with (CtxManager() as example):
      ...
    
    with (
      CtxManager1(), CtxManager2()
    ):
      ...
    
    with (CtxManager1() as example, CtxManager2()):
      ...
    
    with (CtxManager1(), CtxManager2() as example):
      ...
    
    with (
      CtxManager1() as example1,
      CtxManager2() as example2,
    ):
      ...
  7. 数据类:slots、kw_only
    数据类装饰器现在支持以下功能:

    • kw_only=True__init__ 中的所有参数都将被标记为仅限关键字参数。
    • slots=True 生成的数据类将使用 __slots__ 来存储数据。

Python 3.11(生命周期结束 2027年10月)

  1. Tomllib

    tomllib - 标准库TOML解析器

  2. 异常组(PEP 654)

    PEP 654引入了一些语言特性,使程序能够同时引发和处理多个不相关的异常。内置类型ExceptionGroupBaseExceptionGroup使得对异常进行分组并一起引发成为可能,而新的except*语法则对except进行了扩展,以匹配异常组的子组。

  3. 用注释丰富异常(PEP 678)
    add_note()方法被添加到BaseException中。它可用于为异常添加在异常抛出时无法获取的上下文信息。添加的注释会出现在默认的回溯中。

    python 复制代码
    try:
      do_something()
    except BaseException as e:
      e.add_note("this happened during do_something")
      raise
  4. 类型提示:Self(PEP 673)

    python 复制代码
    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
  5. 类型标注:LiteralString(PEP 675)

    新的LiteralString注解可用于表明函数参数可以是任何字面量字符串类型。这使得函数能够接受任意的字面量字符串类型,以及由其他字面量字符串创建的字符串。这样,类型检查器就可以确保像执行SQL语句或shell命令这类敏感函数只能通过静态参数调用,从而提供针对注入攻击的保护。

  6. 类型提示:将TypedDict条目标记为[非]必需项(PEP 655)

    python 复制代码
    # 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
  7. 类型提示:通过TypeVarTuple实现可变参数泛型(PEP 646)

    PEP 484 之前引入了 TypeVar,支持创建用单一类型参数化的泛型。PEP 646 新增了 TypeVarTuple,支持用任意数量的类型进行参数化。换句话说,TypeVarTuple 是一种可变类型变量,支持可变泛型。

    这实现了各种各样的用例。特别是,它允许NumPy和TensorFlow等数值计算库中的类数组结构通过数组形状进行参数化。静态类型检查器现在能够捕获使用这些库的代码中与形状相关的错误。

  8. 类型标注:@dataclass_transform(PEP 681)
    dataclass_transform可用于修饰类、元类或本身是装饰器的函数。@dataclass_transform()的存在会告知静态类型检查器,被修饰的对象会执行运行时"魔术"来转换类,使其具有dataclass般的行为。

    python 复制代码
    # 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
  9. 星号解包表达式允许用于for语句中

    这是官方支持的语法

    python 复制代码
    for x in *a, *b:
      print(x)

Python 3.12(终止支持时间:2028年10月)

  1. 类型标注:类型参数语法(PEP 695)

    泛型类和函数的简洁注解

    python 复制代码
    def max[T](args: Iterable[T]) -> T:
      ...
    
    class list[T]:
      def __getitem__(self, index: int, /) -> T:
        ...
    
      def append(self, element: T) -> None:
        ...
  2. 能够使用type语句声明类型别名(生成TypeAliasType)

    python 复制代码
    type Point = tuple[float, float]
    
    # Type aliases can also be generic
    type Point[T] = tuple[T, T]
  3. F字符串的变化(PEP 701)

    f字符串中的表达式组件现在可以是任何有效的Python表达式,包括与包含该f字符串使用相同引号的字符串、多行表达式、注释、反斜杠和Unicode转义序列。

    python 复制代码
    ## 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)}"
  4. 缓冲区协议(PEP 688)

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

    新的collections.abc.Buffer ABC提供了一种表示缓冲区对象的标准方式,例如在类型注解中。新的inspect.BufferFlags枚举表示可用于自定义缓冲区创建的标志。

  5. 类型提示:用于kwargs类型提示的Unpack(PEP 692)

    python 复制代码
    from typing import TypedDict, Unpack
    
    class Movie(TypedDict):
      name: str
      year: int
    
    def foo(**kwargs: Unpack[Movie]):
      ...
  6. 类型提示:override 装饰器(PEP 698)

    确保子类重写的方法确实存在于父类中。

    python 复制代码
    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"

    注意:这与@overload装饰器不同。

Python 3.13(终止支持时间:2029年10月)

虽然这个Python版本没有包含很多新的语言特性,但它确实有一个相当长的变更日志,其中包含了对标准库的许多更改(包括移除某些内容)、更多受支持的平台(iOS和Android)、为locals()定义的语义以及一个改进后的交互式解释器。

  1. 自由线程(PEP 703)、即时编译(PEP 744)

    虽然这并非语言层面的变更,但对大多数人而言,这是一个具有头条新闻价值的变化。

    CPython 3.13 具备无需全局解释器锁(GIL)运行或使用即时编译器(JIT compiler)运行的能力。

  2. static_attributes(静态属性)

    存储通过类体中任何函数内的self.<name>访问的属性名称。

    python 复制代码
    class A:
        def __init__(self, x: int, y: int):
            self.items = [x,y]
            self.max = max(x, y)
            return
    
      # A.__static_attributes__ = ("items", "max")
  3. 文档字符串:常见的前导空白已去除

    python 复制代码
    def spam():
        """
            This is a docstring with
              leading whitespace.
    
            It even has multiple paragraphs!
        """
    #   ^^^^ This white space is stripped
    # spam.__doc__ = '\nThis is a docstring with\n  leading whitespace.\n\nIt even has multiple paragraphs!\n'
  4. 类型标注:类型参数默认值(PEP 696)

    允许为TypeVarParamSpecTypeVarTuple设置默认值。更多示例请参见PEP。

    python 复制代码
    DefaultBoolT = TypeVar("DefaultBoolT", default=bool)
    T = TypeVar("T")
    
    class OneDefault(Generic[T, DefaultBoolT]): ...
    
    OneDefault[float] == OneDefault[float, bool]  # Valid
  5. 类型提示:warnings支持类型弃用(PEP 702)
    warnings模块中新增了一个新的装饰器@deprecated()。这个装饰器可用于类、函数或方法,以标记它们为已过时。这包括typing.TypedDicttyping.NamedTuple的定义。对于重载函数,该装饰器可应用于各个重载,表明特定的重载已过时。该装饰器也可应用于重载实现函数,表明整个函数已过时。

  6. 类型提示:TypedDict ReadOnly(PEP 705)

    类型限定符typing.ReadOnly用于表示在TypedDict定义中声明的项不得被修改(添加、修改或删除):

    python 复制代码
    class Band(TypedDict):
        name: str
        members: ReadOnly[list[str]]
    
    blur: Band = {"name": "blur", "members": []}
    blur["name"] = "Blur"  # OK: "name" is not read-only
    blur["members"] = ["Damon Albarn"]  # Type check error: "members" is read-only
    blur["members"].append("Damon Albarn")  # OK: list is mutable
  7. 类型标注:TypeIs(PEP 742)

    TypeGuard的替代方案,用于对执行类型收窄的函数进行注解。

    TypeIs和TypeGuard在以下方面有所不同:

    • TypeIs要求缩小后的类型是输入类型的子类型,而TypeGuard则不要求。主要原因是为了允许将list[object]缩小为list[str]之类的操作,尽管后者并不是前者的子类型,因为list是不变的。
    • 当TypeGuard函数返回True时,类型检查器会将变量的类型精确地收窄为TypeGuard类型。当TypeIs函数返回True时,类型检查器可以结合变量先前已知的类型和TypeIs类型,推断出更精确的类型。(从技术上讲,这被称为交集类型。)
    • 当TypeGuard函数返回False时,类型检查器完全无法缩小变量的类型范围。当TypeIs函数返回False时,类型检查器可以缩小变量的类型范围,以排除TypeIs类型。
    python 复制代码
    class Parent: ...
    class Child(Parent): ...
    class Unrelated: ...
    
    def is_parent(val: object) -> TypeIs[Parent]:
        return isinstance(val, Parent)
    
    def run(arg: Child | Unrelated):
        if is_parent(arg):
            # Type of ``arg`` is narrowed to the intersection
            # of ``Parent`` and ``Child``, which is equivalent to
            # ``Child``.
            assert_type(arg, Child)
        else:
            # Type of ``arg`` is narrowed to exclude ``Parent``,
            # so only ``Unrelated`` is left.
            assert_type(arg, Unrelated)

Python 3.14(终止支持时间:2030年10月)

和往常一样,有一长串非语言方面的更改,因此建议您阅读发布说明。

  1. 模板字符串(PEP 750)

    模板字符串字面量(t字符串)会生成Template对象,这些对象随后可以被单独操作。

    python 复制代码
    from string.templatelib import Template
    
    name = "World"
    template: Template = t"Hello {name}!"
    
    print(template.strings)
    ('Hello ', '!')
    
    print(template.values)
    ('World',)
  2. 注解的延迟求值(PEP 649、PEP 749)

    注解不再被立即计算,而是存储在专用的注解函数中。新添加的annotationlib模块允许与这些函数进行交互。请注意,使用from __future__ import annotations会对此产生影响。

    这意味着从Python 3.14开始,对于使用前向引用的注解,你不再需要添加引号或使用future导入。

    python 复制代码
    ## BEFORE
    # String Quoting
    class A:
      def some_func(self, *args) -> 'B | None':
        ...
    
    class B:
      ...
    
    # future import
    from __future__ import annotations
    
    class A:
      def some_func(self, *args) -> B | None:
        ...
    
    class B:
      ...
    
    ## PYTHON 3.14
    class A:
      def some_func(self, *args) -> B | None:  # Just Works (TM)
        ...
    
    class B:
      ...
  3. 允许不带括号的except和except(PEP 758) *

    现在,exceptexcept*表达式在存在多个异常类型且未使用as子句时,允许省略括号。例如,以下表达式现在是有效的:

    python 复制代码
    try:
        connect_to_server()
    except TimeoutError, ConnectionRefusedError:
        print("Network issue encountered.")
    
    # The same applies to except* (for exception groups):
    try:
        connect_to_server()
    except* TimeoutError, ConnectionRefusedError:
        print("Network issue encountered.")
  4. 其他变更

    • 自由线程Python现已正式支持(但仍是可选的)。
    • 现在有多个解释器可用,它们位于concurrent.interpreters模块中。过去,这些解释器只能通过C语言API获取。
    • returnbreakcontinue语句退出finally块时,编译器会发出SyntaxWarning。(PEP 765)

参考

相关推荐
hqwest2 小时前
码上通QT实战29--系统设置04-用户操作管理
开发语言·qt·模态窗体·addbindvalue·bindvalue
傻啦嘿哟2 小时前
Python中的@property:优雅控制类成员访问的魔法
前端·数据库·python
专注于大数据技术栈2 小时前
java学习--LinkedHashSet
java·开发语言·学习
这个图像胖嘟嘟2 小时前
前端开发的基本运行环境配置
开发语言·javascript·vue.js·react.js·typescript·npm·node.js
sky17203 小时前
VectorStoreRetriever 三种搜索类型
python·langchain
星竹晨L3 小时前
【C++内存安全管理】智能指针的使用和原理
开发语言·c++
宵时待雨3 小时前
数据结构(初阶)笔记归纳3:顺序表的应用
c语言·开发语言·数据结构·笔记·算法
旦莫3 小时前
Python测试开发工具库:日志脱敏工具(敏感信息自动屏蔽)
python·测试开发·自动化·ai测试
唐叔在学习3 小时前
Python自动化指令进阶:UAC提权
后端·python