20.2. importlib — Python 的模块载入机制

未匹配的标注

目的: importlib 模块公开了 Python 中 import 语句的实现

importlib 模块包含实现 Python 导入机制的函数,用于在包和模块中加载代码。 它是动态导入模块的一个访问点,在编写代码时需要导入的模块名称未知的情况下很有用(例如,对于应用程序的插件或扩展)。

示例包

本节的示例使用了一个包
称为 example ,包含有  __init__.py

example/init.py

print('Importing example package')

这个包还包含 submodule.py

example/submodule.py

print('Importing submodule')

导入包或模块时,请注意示例中 print() 调用输出的文本。

模块类型

Python 支持多种样式的模块。 打开模块并将其添加到命名空间时,每个模块都需要自己处理,并且格式的支持因平台而异。 例如,在 Microsoft Windows 下,共享库是从扩展名为 .dll.pyd 的文件加载而不是 .so 。 当使用解释器的调试版本而不是正常版本构建时,C 模块的扩展也可能会发生变化,因为它们也可以进行包含的调试信息编译。如果 C 扩展库或其他模块未按预期加载,请使用 importlib.machinery 中定义的常量来查找当前平台支持的类型以及加载它们的参数。

importlib_suffixes.py

import importlib.machinery

SUFFIXES = [
    ('Source:', importlib.machinery.SOURCE_SUFFIXES),
    ('Debug:',
     importlib.machinery.DEBUG_BYTECODE_SUFFIXES),
    ('Optimized:',
     importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES),
    ('Bytecode:', importlib.machinery.BYTECODE_SUFFIXES),
    ('Extension:', importlib.machinery.EXTENSION_SUFFIXES),
]

def main():
    tmpl = '{:<10}  {}'
    for name, value in SUFFIXES:
        print(tmpl.format(name, value))

if __name__ == '__main__':
    main()

返回值是包含文件扩展名的元组序列,用于打开包含模块的文件的模式,以及来自模块中定义的常量的类型代码。 此表是不完整的,因为某些可导入的模块或包类型不对应到单个文件。

$ python3 importlib_suffixes.py

Source:     ['.py']
Debug:      ['.pyc']
Optimized:  ['.pyc']
Bytecode:   ['.pyc']
Extension:  ['.cpython-36m-darwin.so', '.abi3.so', '.so']

导入模块

importlib 中的高级 API 使得在给定绝对或相对名称的情况下导入模块变得简单。 使用相对模块名称时,将包含模块的包指定为单独的参数。

importlib_import_module.py

import importlib

m1 = importlib.import_module('example.submodule')
print(m1)

m2 = importlib.import_module('.submodule', package='example')
print(m2)

print(m1 is m2)

import_module() 的返回值是导入创建的模块对象。

$ python3 importlib_import_module.py

Importing example package
Importing submodule
<module 'example.submodule' from '.../example/submodule.py'>
<module 'example.submodule' from '.../example/submodule.py'>
True

如果模块不能被导入 import_module() 将引发 ImportError 错误。

importlib_import_module_error.py

import importlib

try:
    importlib.import_module('example.nosuchmodule')
except ImportError as err:
    print('Error:', err)

错误消息包含缺少的模块的名称。

$ python3 importlib_import_module_error.py

Importing example package
Error: No module named 'example.nosuchmodule'

要重新载入已有模块,使用 reload()

importlib_reload.py

import importlib

m1 = importlib.import_module('example.submodule')
print(m1)

m2 = importlib.reload(m1)
print(m1 is m2)

reload() 的返回值是新模块。 根据使用的加载器类型不同,它可能是相同的模块实例。

$ python3 importlib_reload.py

Importing example package
Importing submodule
<module 'example.submodule' from '.../example/submodule.py'>
Importing submodule
True

加载器

importlib 中的低级 API 提供对 loader 对象的访问,如模块和导入sys 模块一节所述。 要获取模块的加载器,请使用 find_loader() 。 然后获取模块,使用 loader 的 load_module() 方法。

importlib_find_loader.py

import importlib

loader = importlib.find_loader('example')
print('Loader:', loader)

m = loader.load_module()
print('Module:', m)

这个示例加载了 example 包的顶层。

$ python3 importlib_find_loader.py

Loader: <_frozen_importlib_external.SourceFileLoader object at
0x101fe1828>
Importing example package
Module: <module 'example' from '.../example/__init__.py'>

包中的子模块需要使用包中的路径单独加载。 在下面的示例中,首先加载包,然后将其路径传递给 find_loader() 以创建能够加载子模块的加载器。

importlib_submodule.py

import importlib

pkg_loader = importlib.find_loader('example')
pkg = pkg_loader.load_module()

loader = importlib.find_loader('submodule', pkg.__path__)
print('Loader:', loader)

m = loader.load_module()
print('Module:', m)

import_module() 不同,子模块的名称应该没有任何相对路径前缀,因为加载器已经受到包路径的约束。

$ python3 importlib_submodule.py

Importing example package
Loader: <_frozen_importlib_external.SourceFileLoader object at
0x101fe1f28>
Importing submodule
Module: <module 'submodule' from '.../example/submodule.py'>

参见

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

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

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

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

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


暂无话题~