初窥 Python 的 import 机制( 三 )

四、元路径查找器(meta path finder)元路径查找器的工作就是看是否能找到模块 。这些查找器存放在 sys.meta_path 中以供 Python 遍历(当然它们也可以通过 import 勾子返回 , 参见上面的例子) 。每个查找器必须实现 find_spec 方法 。如果一个查找器知道怎么处理将引入的模块 , find_spec 将返回一个 ModuleSpec 对象(参见下节)否则返回 None 。
和之前提到的一样 sys.meta_path 包含三种查找器:

  • 内置模块查找器
  • 冻结模块查找器
  • 基于路径的查找器
这里我们想重点聊一聊基于路径的查找器(path based finder) 。它用于搜索一系列 import 路径 , 每个路径都用来查找是否有对应的模块可以加载 。默认的路径查找器实现了所有在文件系统的特殊文件中查找模块的功能 , 这些特殊文件包括 Python 源文件(.py 文件) , Python 编译后代码文件(.pyc 文件) , 共享库文件(.so 文件) 。如果 Python 标准库中包含 zipimport , 那么相关的文件也可用来查找可引入的模块 。
路径查找器不仅限于文件系统中的文件 , 它还可以上 URL 数据库的查询 , 或者其他任何可以用字符串表示的地址 。
你可以用上节提供的勾子来实现对同类型地址的模块查找 。例如 , 如果你想通过 URL 来 import 模块 , 那么你可以写一个 import 勾子来解析这个 URL 并且返回一个路径查找器 。
注意 , 路径查找器不同于元路径查找器 。后者在 sys.meta_path 中用于被 Python 遍历 , 而前者特指基于路径的查找器 。
五、ModuleSpec 对象每个元路径查找器必须实现 find_spec 方法 , 如果该查找器知道如果处理要引入的模块 , 那么这个方法将返回一个 ModuleSpec 对象 。这个对象有两个属性值得一提 , 一个是模块的名字 , 而另一个则是查找器 。如果一个 ModuleSpec 对象的查找器是 None , 那么类似 ImportError: missing loader 的异常将会被抛出 。查找器将用来创建和执行一个模块(见下节) 。
你可以通过 <module>.__spec__ 来查找模块的 ModuleSpec 对象:
In [1]: import sysIn [2]: sys.__spec__Out[2]: ModuleSpec(name='sys', loader=<class '_frozen_importlib.BuiltinImporter'>)六、加载器(loader)加载器通过 create_module 来创建模块以及 exec_module 来执行模块 。通常如果一个模块是一个 Python 模块(非内置模块或者动态扩展) , 那么该模块的代码需要在模块的 __dict__ 空间上执行 。如果模块的代码无法执行 , 那么就会抛出ImportError 异常 , 或者其他在执行过程中的异常也会被抛出 。
绝大多数情况下 , 查找器和加载器是同一个东西 。这种情况下 , 查找器的 find_spec 方法返回的 ModuleSpec 对象的 loader 属性将指向它自己 。
我们可以用 create_module 来动态创建一个模块 , 如果它返回 None Python 会自动创建一个模块 。
七、总结Python 的 import 机制灵活而强大 。以上的介绍大部分是基于官方文档 , 以及较新的 Python 3.6+ 版本 。由于篇幅 , 还有很多细节并没有包含其中 , 例如子模块的加载、模块代码的缓存机制等等 。文章中也难免出现纰漏如果有任何问题 , 欢迎到开 issue 提问及讨论 。




推荐阅读