Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 273 – 从 ZIP 压缩包中导入模块

作者:
James C. Ahlstrom <jim at interet.com>
状态:
最终
类型:
标准跟踪
创建:
2001-10-11
Python 版本:
2.3
历史记录:
2001-10-26

目录

摘要

此 PEP 添加了从 ZIP 压缩包中导入 Python 模块 *.py*.py[co] 和包的能力。如果提供了 os.listdir,则使用相同的代码来加速普通目录导入。

注意

ZIP 导入已添加到 Python 2.3 中,但最终的实现使用的方法与本 PEP 中描述的不同。2.3 实现是 SourceForge 修补程序 #652586 [1],它添加了在 PEP 302 中描述的新的导入钩子。

因此,本 PEP 的其余部分仅具有历史意义。

规范

当前,sys.path 是一个由字符串组成的目录名称列表。如果此 PEP 被实现,sys.path 的一项可以是一个命名 ZIP 文件压缩包的字符串。ZIP 压缩包可以包含子目录结构来支持包导入。ZIP 压缩包与子目录完全一样满足导入。

该实现位于 Python 核心中的 C 代码中,并在所有支持的 Python 平台上运行。

ZIP 压缩包中可以存在任何文件,但只有文件 *.py*.py[co] 可用于导入。禁止动态模块 (*.pyd*.so) 的 ZIP 导入。

就像 sys.path 当前具有默认目录名称一样,也添加了一个默认的 ZIP 压缩包名称。否则,无法从压缩包中导入所有 Python 库文件。

子目录等价性

ZIP 压缩包必须完全像子目录树一样对待,这样我们才能根据当前和将来的规则支持包导入。所有 ZIP 数据都来自中央目录,数据必须正确,并且不考虑脑残的 ZIP 文件。

假设 sys.path 包含 “/A/B/SubDir” 和 “/C/D/E/Archive.zip”,并且我们正在尝试从 Q 包中导入 modfoo。那么 import.c 将生成一个路径和扩展名列表,并将查找该文件。生成的路径列表对于 ZIP 导入没有变化。假设 import.c 生成了路径 “/A/B/SubDir/Q/R/modfoo.pyc”。那么它也将生成路径 “/C/D/E/Archive.zip/Q/R/modfoo.pyc”。查找 SubDir 路径等同于在压缩包中查找 “Q/R/modfoo.pyc”。

假设你将 /A/B/SubDir/* 及其所有子目录压缩在一起。那么你的 ZIP 文件将满足导入,就像你的子目录一样。

好吧,不完全是。你无法从 ZIP 文件中满足动态模块。动态模块的扩展名如 .dll.pyd.so。它们依赖于操作系统,可能只能从文件中加载。可能可以从 ZIP 文件中提取动态模块,将其写入普通文件并加载它。但这将意味着创建临时文件,并处理所有 dynload_*.c,这可能不是一个好主意。

当尝试导入 *.pyc 时,如果它不可用,则将改用 *.pyo。反之亦然,当查找 *.pyo 时。如果 *.pyc*.pyo 都不可用,或者如果魔数无效,则将编译 *.py 并使用它来满足导入,但不会保存编译后的文件。Python 通常会将其写入与 *.py 相同的目录,但我们当然不想写入 ZIP 文件。我们可以写入 ZIP 压缩包的目录,但这会使它变得混乱,如果它是 /usr/bin,则不好。

无法写入编译后的文件将使 ZIP 导入变得非常慢,用户可能无法弄清楚问题所在。因此,最好将 *.pyc*.pyo*.py 一起放入压缩包中。

效率

在 ZIP 压缩包中查找文件的唯一方法是线性搜索。因此,对于 sys.path 中的每个 ZIP 文件,我们都会搜索其名称一次,并将名称和其他相关数据放入一个静态 Python 字典中。键是 sys.path 中的压缩包名称与压缩包内的文件名(包括任何子目录)连接在一起。这正是 import.c 生成的名称,并使查找变得容易。

这种机制也用于加速目录(非 ZIP)导入。见下文。

zlib

压缩的 ZIP 压缩包需要 zlib 来解压缩。在任何其他导入之前,我们尝试导入 zlib。除非 zlib 可用,否则压缩文件的导入将失败,并显示一条消息 “缺少 zlib”。

启动

Python 导入 site.py 本身,这将导入 osntntpathstatUserDict。它还导入 sitecustomize.py,它可能导入更多模块。ZIP 导入必须在导入 site.py 之前可用。

就像 sys.path 中存在默认目录一样,也必须存在一个或多个默认 ZIP 压缩包。

问题是名称应该是什么。名称应与 Python 版本相关联,以便 Python 可执行文件即使在同一台机器上存在多个 Python 版本时也能正确找到其相应的库。

我们在 sys.path 中添加了一个名称。在 Unix 上,目录为 sys.prefix + "/lib",文件名是 "python%s%s.zip" % (sys.version[0], sys.version[2])。因此,对于 Python 2.2 和前缀 /usr/local,路径 /usr/local/lib/python2.2/ 已经在 sys.path 上,并将添加 /usr/local/lib/python22.zip。在 Windows 上,该文件是 python22.dll 的完整路径,其中 “dll” 被替换为 “zip”。ZIP 压缩包名称始终作为 sys.path 中的第二项插入。第一个是 main.py 的目录(感谢 Tim)。

目录导入

用于加速 ZIP 导入的静态 Python 字典也可以用于加速普通目录导入。对于 sys.path 中不是 ZIP 压缩包的每一项,我们都会调用 os.listdir,并将目录内容添加到字典中。然后,我们只需检查字典,而不是在双循环中调用 fopen()。这大大加快了导入速度。如果 os.listdir 不存在,则不会使用该字典。

基准测试

案例 原始 2.2a3 使用 os.listdir ZIP 未压缩 ZIP 压缩
1 3.2 2.5 3.2->1.02 2.3 2.5 2.3->0.87 1.66->0.93 1.5->1.07
2 2.8 3.9 3.0->1.32 与案例 1 相同。
3 5.7 5.7 5.7->5.7 2.1 2.1 2.1->1.8 1.25->0.99 1.19->1.13
4 9.4 9.4 9.3->9.35 与案例 3 相同。

案例 1:本地驱动器 C:,sys.path 具有其默认值。案例 2:本地驱动器 C:,包含文件的目录位于 sys.path 的末尾。案例 3:网络驱动器,sys.path 具有其默认值。案例 4:网络驱动器,包含文件的目录位于 sys.path 的末尾。

基准测试是在奔腾 4 克隆机上进行的,频率为 1.4 GHz,内存为 256 MB。该机器运行的是 Windows 2000,并带有 Linux/Samba 网络服务器。时间以秒为单位,是导入约 100 个 Lib 模块所需的时间。案例 2 和 4 将 “正确” 目录移动到 sys.path 的末尾。 “未压缩” 表示未压缩的 ZIP 压缩包, “压缩” 表示压缩的压缩包。

初始时间是在系统重新启动后; “->” 后的时间是在重复运行后的时间。从 C: 导入的重新启动后的时间对于 “原始” 案例来说变化很大,但更现实。

自定义导入

该逻辑演示了使用默认搜索导入的能力,直到需要 Python 模块(在本例中为 os)可用。这可以用于引导自定义导入器。例如,如果 __init__.py 中存在 “importer()”,则可以使用它来进行导入。 “importer()” 可以自由地导入 os 和其他模块,这些模块将通过默认机制得到满足。此 PEP 没有定义任何自定义导入器,此注释仅供参考。

实现

C 实现可作为 SourceForge 补丁 492105 获得。由补丁 652586 和当前 CVS 取代。 [2]

较新的版本(由 Paul Moore 更新为最新的 CVS)是 645650。由补丁 652586 和当前 CVS 取代。 [3]

Just van Rossum 提供的另一个实现是 652586,它是最终实现 PEP 302 的基础。 PEP 273 是使用 PEP 302 的导入钩子实现的。 [1]

参考资料


来源: https://github.com/python/peps/blob/main/peps/pep-0273.rst

上次修改: 2023-09-09 17:39:29 GMT