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 的末尾。

基准测试是在一台 Pentium 4 克隆机上进行的,1.4 GHz,256 MB。该机器运行 Windows 2000,带有一个 Linux/Samba 网络服务器。时间以秒为单位,是导入大约 100 个 Lib 模块所需的时间。情况 2 和 4 将“正确”目录移动到 sys.path 的末尾。“Uncomp”表示未压缩的 zip 压缩包,“Compr”表示已压缩的 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

最后修改:2025-02-01 08:55:40 GMT