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实现在加载模块时 打印查找和加载的信息:
load_module 方法返回一个 module 对象,这个对象就是 import 的 module 对象了。 比如我上面那样就把 http 替换为 sys 这个 module 了。
它的实现原理涉及了以下几个知识点:
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动态类型的学习---引用的理解
- Python3写爬虫(四)多线程实现数据爬取
- 垃圾邮件过滤器 python简单实现
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- install and upgrade scrapy
- Scrapy的架构介绍
- Centos6 编译安装Python
- 使用Python生成Excel格式的图片
- 让Python文件也可以当bat文件运行
- [Python]推算数独
- Python中zip()函数用法举例
- Python中map()函数浅析
- Python将excel导入到mysql中
- Python在CAM软件Genesis2000中的应用
- 使用Shiboken为C++和Qt库创建Python绑定
- FREEBASIC 编译可被python调用的dll函数示例
- Python 七步捉虫法