20.4. zipimport — 从 Zip 文件中加载 Python 代码
目的:导入保存为 ZIP 压缩包内容的 Python 模块。
zipimport
模块实现了 zipimporter
类,可用于在 ZIP 压缩包中查找和加载 Python 模块。 zipimporter
支持 PEP 302 中指定的导入钩子 API ;这就是 Python Eggs 的工作方式。
通常不需要直接使用 zipimport
模块,因为只要该压缩包出现在 sys.path
中,就可以直接从 ZIP 压缩包导入。 但是,研究如何使用导入器 API ,学习可用的功能以及了解模块导入的工作原理是有益的。 了解使用 ZIP 导入程序的工作方式也有助于调试在分发打包为使用 zipfile.PyZipFile
创建的 ZIP 压缩包储存的应用程序时可能出现的问题。
示例
这些示例重用了 zipfile
讨论中的一些代码来创建包含一些 Python 模块的示例 ZIP 压缩包。
zipimport_make_example.py
import sys
import zipfile
if __name__ == '__main__':
zf = zipfile.PyZipFile('zipimport_example.zip', mode='w')
try:
zf.writepy('.')
zf.write('zipimport_get_source.py')
zf.write('example_package/README.txt')
finally:
zf.close()
for name in zf.namelist():
print(name)
在任何其他示例之前运行 zipimport_make_example.py
以创建包含示例目录中所有模块的 ZIP 压缩包,以及本节中示例所需的一些测试数据。
$ python3 zipimport_make_example.py
__init__.pyc
example_package/__init__.pyc
zipimport_find_module.pyc
zipimport_get_code.pyc
zipimport_get_data.pyc
zipimport_get_data_nozip.pyc
zipimport_get_data_zip.pyc
zipimport_get_source.pyc
zipimport_is_package.pyc
zipimport_load_module.pyc
zipimport_make_example.pyc
zipimport_get_source.py
example_package/README.txt
寻找一个模块
给出模块的全名, find_module()
将尝试在 ZIP 压缩包中找到该模块。
zipimport_find_module.py
import zipimport
importer = zipimport.zipimporter('zipimport_example.zip')
for module_name in ['zipimport_find_module', 'not_there']:
print(module_name, ':', importer.find_module(module_name))
如果找到该模块,则返回 zipimporter
实例。 否则,返回 None
。
$ python3 zipimport_find_module.py
zipimport_find_module : <zipimporter object
"zipimport_example.zip">
not_there : None
访问代码
get_code()
方法从归档中加载模块的代码对象。
zipimport_get_code.py
import zipimport
importer = zipimport.zipimporter('zipimport_example.zip')
code = importer.get_code('zipimport_get_code')
print(code)
code 对象与 module
对象不同,但用于创建一个 module 对象。
$ python3 zipimport_get_code.py
<code object <module> at 0x1012b4ae0, file
"./zipimport_get_code.py", line 6>
要将代码加载为可用模块,请使用 load_module()
代替。
zipimport_load_module.py
import zipimport
importer = zipimport.zipimporter('zipimport_example.zip')
module = importer.load_module('zipimport_get_code')
print('Name :', module.__name__)
print('Loader :', module.__loader__)
print('Code :', module.code)
结果是模块对象配置为类似代码从常规导入加载。
$ python3 zipimport_load_module.py
<code object <module> at 0x1007b4c00, file
"./zipimport_get_code.py", line 6>
Name : zipimport_get_code
Loader : <zipimporter object "zipimport_example.zip">
Code : <code object <module> at 0x1007b4c00, file
"./zipimport_get_code.py", line 6>
源
与 inspect
模块一样,如果存档包含源,则可以从 ZIP 压缩包中检索模块的源代码。 在示例的情况下,只有 zipimport_get_source.py
被添加到 zipimport_example.zip
(其余的模块只是作为 .pyc
文件添加)。
zipimport_get_source.py
import zipimport
modules = [
'zipimport_get_code',
'zipimport_get_source',
]
importer = zipimport.zipimporter('zipimport_example.zip')
for module_name in modules:
source = importer.get_source(module_name)
print('=' * 80)
print(module_name)
print('=' * 80)
print(source)
print()
如果模块的源不可用, get_source()
返回 None
。
$ python3 zipimport_get_source.py
================================================================
zipimport_get_code
================================================================
None
================================================================
zipimport_get_source
================================================================
#!/usr/bin/env python3
#
# Copyright 2007 Doug Hellmann.
#
"""检索 zip 压缩包中模块的源代码。
"""
#end_pymotw_header
import zipimport
modules = [
'zipimport_get_code',
'zipimport_get_source',
]
importer = zipimport.zipimporter('zipimport_example.zip')
for module_name in modules:
source = importer.get_source(module_name)
print('=' * 80)
print(module_name)
print('=' * 80)
print(source)
print()
包
要确定名称是否引用包而不是常规模块,请使用 is_package()
。
zipimport_is_package.py
import zipimport
importer = zipimport.zipimporter('zipimport_example.zip')
for name in ['zipimport_is_package', 'example_package']:
print(name, importer.is_package(name))
在这种情况下, zipimport_is_package
来自一个模块, example_package
是一个包。
$ python3 zipimport_is_package.py
zipimport_is_package False
example_package True
数据
有时需要使用非代码数据分发源模块或包。 图像,配置文件,默认数据和测试夹具只是其中的几个例子。 通常,模块 __path__
或 __file__
属性用于查找与安装代码的位置相关的这些数据文件。
例如,使用「普通」模块,可以从导入的包的 __file__
属性构造文件系统路径,如下所示:
zipimport_get_data_nozip.py
import os
import example_package
# 找到包含导入包的目录
# 并从中构建数据文件名。
pkg_dir = os.path.dirname(example_package.__file__)
data_filename = os.path.join(pkg_dir, 'README.txt')
# 读取文件并显示其内容。
print(data_filename, ':')
print(open(data_filename, 'r').read())
输出将取决于示例代码在文件系统上的位置。
$ python3 zipimport_get_data_nozip.py
.../example_package/README.txt :
This file represents sample data which could be embedded in the
ZIP archive. You could include a configuration file, images, or
any other sort of noncode data.
如果从 ZIP 压缩包而不是文件系统导入 example_package
,则使用 __file__
不起作用。
zipimport_get_data_zip.py
import sys
sys.path.insert(0, 'zipimport_example.zip')
import os
import example_package
print(example_package.__file__)
data_filename = os.path.join(
os.path.dirname(example_package.__file__),
'README.txt',
)
print(data_filename, ':')
print(open(data_filename, 'rt').read())
包的 __file__
引用 ZIP 压缩包,而不是目录,因此构建 README.txt
文件的路径会给出错误的值。
$ python3 zipimport_get_data_zip.py
zipimport_example.zip/example_package/__init__.pyc
zipimport_example.zip/example_package/README.txt :
Traceback (most recent call last):
File "zipimport_get_data_zip.py", line 20, in <module>
print(open(data_filename, 'rt').read())
NotADirectoryError: [Errno 20] Not a directory:
'zipimport_example.zip/example_package/README.txt'
检索文件的更可靠方法是使用 get_data()
方法。 可以通过导入模块的 __loader__
属性访问加载模块的 zipimporter
实例:
zipimport_get_data.py
import sys
sys.path.insert(0, 'zipimport_example.zip')
import os
import example_package
print(example_package.__file__)
data = example_package.__loader__.get_data(
'example_package/README.txt')
print(data.decode('utf-8'))
pkgutil.get_data()
使用此接口访问包中的数据。 返回的值是一个字节字符串,需要在打印前解码为 unicode 字符串。
$ python3 zipimport_get_data.py
zipimport_example.zip/example_package/__init__.pyc
This file represents sample data which could be embedded in the
ZIP archive. You could include a configuration file, images, or
any other sort of noncode data.
没有为未通过 zipimport
导入的模块设置 __loader__
。
另请参阅
- zipimport 标准库文档
- Python 2 to 3 porting notes for zipimport
imp
-- 其他与进口相关的功能。pkgutil
-- 为get_data()
提供更通用的接口。zipfile
-- 读写 ZIP 压缩包文件。- PEP 302 -- 新的导入钩子
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。