反向移植
Python 的许多新增功能都提供了针对旧版本 Python 的反向移植。以下是一些使用反向移植的技巧。
- 反向移植是一个非常轻量级的依赖项,因为消除它的方法之一就是升级 Python。
- 当你放弃旧版本的 Python 时,反向移植将不再是依赖项。
- 如果一个包已纳入标准库,那么它应该设计良好、文档齐全,并且可能是人们学习的知识。
- 反向移植不会被新版本的 Python 打破,因为你不会在新版本的 Python 上使用反向移植。
使用反向移植的规则如下。
条件需求
将它有条件地添加到你的需求中。这看起来像这样。
[project]
dependencies = [
"importlib_metadata>=4.6; python_version<'3.10'",
"importlib_resources; python_version<'3.9'",
"typing_extensions>=4.6; python_version<'3.11'",
]
条件使用
始终使用以下习语有条件地使用反向移植。
import sys
if sys.version_info < (3, 10):
import importlib_metadata as metadata
else:
from importlib import metadata
永远不要使用 try/except
来处理反向移植。上述习语具有以下优点。
- 条件导入的原因在代码中表达。你无需添加注释来解释需要此操作来支持 X.Y 版本的 Python;它就在代码中,供读者查看。
- MyPy 等静态分析工具可以理解此检查,并会正确处理。
- pyupgrade 和 Ruff 的 pyupgrade 等静态自动修正器会在你升级 Python 版本时自动删除无用的分支。你也可以手动查看
git grep "sys.version_info"
的输出以清理这些内容。 - 你可以选择要切换到的特定版本的 Python,即使导入在更早的时候就可用。在本例中,
import.metadata
在 3.8 中添加,但重要修复程序在 3.10 中发布。 - 它与你的条件需求相匹配。
文件中的位置
将所有条件反向移植放置在通用位置是一种很好的做法。以下是一个建议:将所有导入放在 src/<package>/_compat
中,采用标准库结构。这在你的代码库中提供了非常简洁、可搜索的导入,看起来与正常使用类似。
例如,你可以有一个文件 src/<package>/_compat/typing.py
,其内容如下。
from __future__ import annotations
import sys
if sys.version_info < (3, 10):
from typing_extensions import TypeAlias
else:
from typing import TypeAlias
if sys.version_info < (3, 11):
from typing_extensions import Self, assert_never
else:
from typing import Self, assert_never
__all__ = ["TypeAlias", "Self", "assert_never"]
def __dir__() -> list[str]:
return __all__
Ruff 需要知道你是否正在重新导出 typing
/typing_extensions
,因此请确保在 pyproject.toml
中将 typing-modules = ["<package>._compat.typing"]
添加到 Ruff 的配置中。
类型依赖
虽然通常不需要这样做,但你可以通过用 typing.TYPE_CHECKING
来保护导入,从而在运行时避免使用 typing_extensions
反向移植。 typing_extensions
是一个第一方反向移植,并且非常常用,因此你的依赖项很可能已经拉取了它。但是,如果你真的想要最大程度地减少依赖项,你可以在你的类型反向移植重新导出文件中执行此操作。
常用反向移植包
typing_extensions
:typing
中的新功能首先在这里添加。importlib_metadata
:在 3.8 中作为importlib.metadata
添加,在 3.10 中进行了重要更新(并且不再是临时添加)。importlib_resources
:在 3.7 中作为importlib.resources
添加,在 3.9 中进行了重要更新(添加了files
,这是推荐的公共 API!)。tomli
:在 3.11 中作为tomllib
添加。当 TOML 1.1 发布时,它可能会再次变得重要。(请注意,toml_w
不在标准库中)。exceptiongroup
:3.11 中的一个新的内置函数(ExceptionGroup
)。tz-data
:3.9 中zoneinfo
的第一方 PyPI 版本,但具有更新的时区信息。