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__

另请参阅

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/zipimpor...

译文地址:https://learnku.com/docs/pymotw/zipimpor...

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~