您的位置:首页 > 编程语言 > Python开发

Python 导入机制 - import hook

2016-07-26 18:31 218 查看
Python import hook可以翻译为Python 探针。

它的实现原理涉及了以下几个知识点:

1. Python导入协议

2. sys.meta_path

一,Python导入协议

Python 中所有加载到内存的模块都放在 sys.modules。当import 一个模 块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则 只是将模 块的名字加入到正在调用 import 的模块的 Local 名字空间中。如果没有加载则 从sys.path 目录中按照模块名称查找模块文件,模块文件可以是 py、pyc、pyd,找到后将模块载入内存,并加入到 sys.modules 中,一般如果 指定模块没在 sys.modules 中找到,将调用Python 的导入协议来查找和加载 模块。

python 导入协议机制是由查找器finder和加载器loader构成。

查找器finder的职责是找到指定的模块,然后提供一个加载器来处理实际的导入 行为。要注册一个查找器只需要在 sys.path_hooks 列表中。查找器不真正加载 模块。如果他们能够找到指定的模块,他们返回一个模块分支,即模块导入相 关信息的封装,在加载模块时导入机制运用的信息。

查找器对象finder需要实现如下find_module 方法:

finder.find_module(fullname, path=None)1

• 如果 finder 被装入了 meta_path,这个方法会接受第二个参数,

• 如果 Import 的是顶层模块:None,

• 如果 import 的是子模块或子包:PATH

• 如果找到模块需要返回一个 loader 对象,没找到需要返回 None

加载器loader需要实现如下load_module 方法

loader.load_module(fullname):

• 这个函数返回一个被载入的模块或者抛出异常

• 判断传入的 fullname 是不是已经在 sys.modules 中了,如果在的话loader 必须使用已经载入的模块,否则 reload 功能不能正常工作,如果fullname 不在 sys.modules 中,loader 必须新建一个 module 并载入

• 在 Loader 执行导入的模块代码之前,模块必须已经被导入 sys.modules中,否则会引起无穷迭代

• 如果导入失败,loader 必须清除已经插入到 sys.modules 中的模块

通过自己实现这样的包括查找器和加载器钩子程序能够实现对 python 导入行为的控制,扩展Python加载模块的功能。

二。sys.meta_path

python 模块是通过import的方式引用的,当我们执行一行 from package import module as mymodule 命令时,Python解释器会查找package这个包的module模块,并将该模块作为mymodule引入到当前的工作空间。import语句主要是做了二件事: 查找相应的module 和加载module到local namespace 。

在import的第一个阶段,主要是完成了查找要引入模块的功能,这个查找的过程如下

1. 检查 sys.modules列表 (保存了之前import的类库的缓存),如果module被找到,则⾛到第二步。

2. 检查 sys.meta_path列表。meta_path 是一个 list,⾥面保存着一些 finder 对象,如果找到该module的话,就会返回一个finder对象。

3. 检查某些隐式的finder对象,不同的python实现有不同的隐式finder,但是都会有 sys.path_hooks, sys.path_importer_cache 以及sys.path。

4. 抛出 ImportError

从上面的过程中可以看出,当执行 import 相关的操作时,会触发 sys.meta_path 列表中定义的finder对象。按照Python导入协议的规则,我们需要实现一个import hook,只需要实现一个finder对象和loader对象对应的find_module方法和load_module方法。要让Python解释器import 模块的 时候触发这个finder对象,只需要将这个对象的实例插入到sys.metapath列表中。

下面是一个简单的hellp world程序 我们通过import hook实现在加载模块时 打印查找和加载的信息:

import sys

class MetaPathFinder:

def find_module(self, fullname, path=None):
print('find_module {}'.format(fullname))
return MetaPathLoader()

class MetaPathLoader:

def load_module(self, fullname):
print('load_module {}'.format(fullname))
sys.modules[fullname] = sys
return sys

sys.meta_path.insert(0, MetaPathFinder())

if __name__ == '__main__':
import http
print(http)
print(http.version_info)


load_module 方法返回一个 module 对象,这个对象就是 import 的 module 对象了。 比如我上面那样就把 http 替换为 sys 这个 module 了。

$ python meta_path1.py
find_module http
load_module http
<module 'sys' (built-in)>
sys.version_info(major=3, minor=5, micro=1, releaselevel='final', serial=0)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python