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