Python 模块元路径查找器介绍

我被代码海扁署名-非商业-禁演绎
阅读 5:47·字数 1737·发布 
Bilibili 空间
关注 950

前提

阅读本节的前提是对 Python 模块查找器有所掌握,你可以查看Python 模块查找器介绍一节来了解相关信息。

Python 模块元路径查找器

Python 模块元路径查找器(Module Meta Path Finder)默认可以完成以下几项工作,查找内置模块,查找冻结模块,查找基于路径的模块。

事实上,元路径查找器是真正意义上的 Python 模块查找器,在查找定位模块的过程中,所有元路径查找器被依次调,当某个元路径查找器找到模块时,将返回一个描述该模块的对象(整个查找过程结束),否则,将返回None。当所有元路径查找器均返回None时,说明模块无法被定位,这将引发异常ModuleNotFoundError

查看 Python 模块元路径查找器

元路径查找器存储在sys模块的meta_path变量中,你可以在 Python 的交互模式中,输入import syssys.meta_path两行代码,来查看当前的元路径查找器。如果是py文件,则可以改写sys.meta_pathprint(sys.meta_path)

import sys
sys.meta_path
[<_distutils_hack.DistutilsMetaFinder object at>, <class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib_external.PathFinder'>]
print_meta_path.py
# 显示元路径查找器
import sys
print(sys.meta_path)

查找 Python 内置和冻结模块

元路径查找器 BuiltinImporter 和 FrozenImporter,分别用于查找 Python 的内置和冻结模块。当然,在 Python 启动时,一些内置或冻结模块会自动导入,并保存在sys.modules中,他们再次被 BuiltinImporter 或 FrozenImporter 导入的可能性不大,除非对应的缓存被清除。

广义上的 Python “内置模块”可以是冻结模块

如果你将 Python 启动后sys.modules中包含的模块称为“内置模块”,那么“内置模块”可以是冻结模块,基于路径加载的模块,或真正意义上的内置(built-in)模块。比如,广义上的“内置模块”os是一个冻结模块,广义上的“内置模块”sys是一个真正的内置(built-in)模块。

请注意,以上这些情况可能因为 Python 版本的不同而有所变化。

模块

要了解模块缓存,你可以查看Python 模块缓存一段。

在交互模式中,我们导入模块os,使用globals()函数查看全局信息,发现导入的模块os是一个冻结模块。删除冻结模块查找器 FrozenImporter 以及模块ossys.modules中的缓存,再次执行import osglobals(),发现模块os不再是冻结模块。这是因为 FrozenImporter 查找器被删除,os是通过其他查找器被定位的。

import os
globals()
{'os': <module 'os' (frozen)>}
import sys
sys.meta_path
[<_distutils_hack.DistutilsMetaFinder object at>, <class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib_external.PathFinder'>]
del sys.meta_path[2]
del sys.modules['os']
import os
globals()
{'os': <module 'os' from ''>,}

为何有些 Python 模块元路径查找器的类名称包含英文单词 Importer?

BuiltinImporter 和 FrozenImporter 的准确翻译,应该是内置导入器和冻结导入器,但由于导入器同时具有查找器和加载器的功能,因此,也可以称他们为查找器。

查找基于路径的 Python 模块

元路径查找器 PathFinder,也被称为基于路径查找器,用于查找基于模块搜索路径的 Python 模块,这大概是最为常见的情况,当你希望导入第三方或自己编写的模块或包时, PathFinder 将开始工作。

广义上的 Python “内置模块”可以是基于路径加载的模块

通过查询sys.modules可以发现,在“内置模块”中,有不少是通过基于路径查找器来定位的。

实现 Python 基于路径查找器的可拓展性

Python 模块查找器的另一分类,路径条目查找器,是基于路径查找器的具体实现,因此,可以通过编写路径条目查找器,拓展基于路径查找器的功能。比如,定位在网络上存储的 Python 脚本。

模块搜索路径

要了解模块搜索路径,你可以查看Python 模块搜索路径介绍,Python 模块搜索路径中的目录有哪些一节。

Python 模块元路径查找器的优先级

如前所述,在查找模块时,元路径查找器是被依次调用的,如果他们在sys.meta_path中的顺序被改变,那么可能会引发意想不到的问题。

下面,我们编写了一个自己的os模块,将命令行切换至该模块所在的目录,然后进入 Python 交互模式,把优先级较低的 PathFinder 查找器放到列表sys.meta_path的首位,清除os模块的缓存并重新导入,优先级最高的 PathFinder 将找到我们编写的os模块。

os.py
print('我不是那个 os 模块!')
import sys
sys.meta_path
[<_distutils_hack.DistutilsMetaFinder object at>, <class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib_external.PathFinder'>]
sys.meta_path.reverse()
del sys.modules['os']
import os
我不是那个 os 模块!

源码

print_meta_path.py·codebeatme/python-reference·GitHub
os.py·codebeatme/python-reference·GitHub