Python 模块元路径查找器介绍
前提
阅读本节的前提是对 Python 模块查找器有所掌握,你可以查看Python 模块查找器介绍一节来了解相关信息。
Python 模块元路径查找器
Python 模块元路径查找器(Module Meta Path Finder)默认可以完成以下几项工作,查找内置模块,查找冻结模块,查找基于路径的模块。
事实上,元路径查找器是真正意义上的 Python 模块查找器,在查找定位模块的过程中,所有元路径查找器被依次调,当某个元路径查找器找到模块时,将返回一个描述该模块的对象(整个查找过程结束),否则,将返回None
。当所有元路径查找器均返回None
时,说明模块无法被定位,这将引发异常ModuleNotFoundError
。
查看 Python 模块元路径查找器
元路径查找器存储在sys
模块的meta_path
变量中,你可以在 Python 的交互模式中,输入import sys
和sys.meta_path
两行代码,来查看当前的元路径查找器。如果是py
文件,则可以改写sys.meta_path
为print(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'>]
# 显示元路径查找器
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 以及模块os
在sys.modules
中的缓存,再次执行import os
和globals()
,发现模块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
模块。
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