| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Python知识库 -> python导入系统 -> 正文阅读 |
|
[Python知识库]python导入系统 |
参考 python3.6.15的导入机制文档 前言python的导入系统一直在变化,所以具体版本的导入系统,应该去查看文档。本文并不是文档,本文的目的是为了探究python的模块导入过程。 概念module object:这是python关于模块的唯一的一个数据模型对象,所有的模块都是该对象,不管该模块是一个模块还是一个包。 模块:可以简单的理解为文件系统中的一个.py文件。 包:可以简单的理解为文件系统中的目录,该目录带有一个__init__.py文件。
导入系统的导入方法import语句:from xxxx import xxxx 和import xxxx __import__():这是一个built-in函数。 importlib:该库提供了一系列的用于导入系统的API,importlib.import_module()就是一个专门用于导入模块的API。需要注意的是,该库提供的API,实际上都是导入系统的功能,这里只是给用户提供的使用接口。 几种导入方式对比import语句包含两个过程:第一阶段在内部调用__import__()来搜寻模块并创建模块对象;第二阶段就是绑定名字到该模块对象。 __import__()只是用于搜寻模块并返回模块对象,但是其根据参数的不同或返回顶层模块对象,或返回指定的模块对象。 importlib.imoprt_module()用于搜索指定的模块,并返回指定的模块对象。 # __import__(name,?globals=None,?locals=None,?fromlist=(),?level=0)函数 该函数会导入?name?模块,有可能使用给定的?globals?和?locals?来确定如何在包的上下文中解读名称。?fromlist?给出了应该从由?name?指定的模块导入对象或子模块的名称。 标准实现完全不使用其?locals?参数,而仅使用?globals?参数来确定?import?语句的包上下文。 level?指定是使用绝对还是相对导入。? 当?name?变量的形式为?
# import语句 形式一、import xxxx import spam 这一条语句就相当于如下的__import__调用: spam = __import__('spam', globals(), locals(), [], 0) 返回了spam模块对象之后,python将spam这个模块对象绑定到本地的spam名字。 import spam.ham 这一条语句就相当于如下的__import__调用: spam = __import__('spam.ham', globals(), locals(), [], 0) 仍然返回了spam模块对象,python随后将在本地名字空间中绑定spam名字到spam模块对象。
形式二、from xxx import xxxx from?spam.ham?import?eggs,?sausage?as?saus 这一条语句就相当于如下的__import__调用: _temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0) eggs = _temp.eggs saus = _temp.sausage 在这里,__import__会返回spam.ham模块对象,然后在本地名字空间中绑定了eggs名字到spam.ham.eggs模块对象,同时绑定了saus名字到spam.ham.saus模块对象。
# importlib.import_module() 导入一个模块。参数?name?指定了以绝对或相对导入方式导入什么模块 (比如要么像这样? import_module()?函数是一个对?importlib.__import__()?进行简化的包装器。 这意味着该函数的所有特性都来自于?importlib.__import__()。 这两个函数之间最重要的不同点在于?import_module()?返回指定的包或模块 (例如? 正因为这是对__import__的封装,所以其也会导入父模块,并缓存到sys.modules中。 如果想要动态加载某个模块,importlib.import_module()无疑是最好的选择。 可以看到,这三种方式,实际上最终都是调用__import__函数完成模块的搜寻与模块对象的创建,然后加载执行模块代码。 导入系统的细节__import__会根据参数name来搜寻模块,那么它究竟是怎样搜寻的呢? 其实这取决于三个条件: 1、参数name的值。 2、缓存sys.modules中是否存在该模块。 3、finder与loader。 # 参数name的值 如果name是类似于foo这样的值,那么python只会搜寻foo,并发导入foo。 如果name是类似于foo.bar.baz这样的值,那么python会先搜寻foo,导入foo;再搜寻foo.bar,导入foo.bar;最后搜寻foo.bar.baz,导入foo.bar.baz。 # 模块缓存sys.modules 搜寻模块的第一个地方就是模块缓存sys.modules。 这个映射起到缓存之前导入的所有模块的作用(包括其中间路径)。 因此如果之前导入过? 在导入期间,会在?sys.modules?查找模块名称,如存在则其关联的值就是需要导入的模块,导入过程完成。 然而,如果值为? # finder与loader 当在缓存中找不到对应的模块的时候,就会调用finder去寻找对应的模块对象。 python总共有3个默认的finder: 内置模块finder frozen? module finder 搜索import path的finder
这三种finder的调用顺序,那自然而然是优先调用内置模块finder,然后是frozen module finder,最后是import path finder。
python的导入机制是可扩展的,它的扩展性通过import hooks实现。 # import hooks 导入机制被设计为可扩展;其中的基本机制是 import hooks。 import hooks有两种类型: meta hooks?和 import path hooks。 meta hooks通过在sys.meta_path中添加finder实现,在sys.modules查找完毕之后,meta hooks就能获得执行。meta hooks的运行实际在sys.modules查找之后,在内置模块finder,forzen module finder等之前运行。 import path hooks是作为sys.path(或者package.__path__)过程的一部分,在遇到hooks所hook的路径之后,import hooks获得运行。import hooks通过在sys.path_hooks中添加可调用对象来实现注册。 现在,finder和imoprt hooks都有了,finder可以为用户寻找模块,import hooks给用户提供了自定义载入过程的能力,它们相互配合完成对模块的搜寻,然后通过loader完成对模块的加载与执行。 那么它们是如何 一步步得到调用的呢? # meta path 当指定名称的模块在?sys.modules?中找不到时,Python 会接着搜索?sys.meta_path,其中包含meta_path finder对象列表。 这些finder按顺序被查询,以确定它们是否知道如何处理该名称的模块。meta_path finder必须实现名为?find_spec()?的方法,该方法接受三个参数:名称、导入路径和目标模块(可选)。 meta_path finder可使用任何策略来确定它是否能处理指定名称的模块。 如果meta_path finder知道如何处理指定名称的模块,它将返回一个module spec。 如果它不能处理该名称的模块,则会返回?
对于前面两种finder来说,由于python虚拟机一旦编译完成之后,基本不会有太多更改,因此,其行为也就基本固定了;对于第三种finder来说,其行为往往与path相关,那么它的path搜索范围从哪里来呢? # import path finder imoprt path finder是一个path based finder,它会搜索一个imoprt path,该import path包含一个path entries列表,每一个path entry都表示了一个模块的搜索路径。 该finder会遍历路径列表的路径,为每一个path entry调用path entry finder。 path entry finder最终完成对路径的查找。 正如上面所说,path entry finder最终完成对路径的查找,路径来自path entries 列表,那么import path 中的path entries列表又从哪里来呢? import path中的path entries列表在python导入系统初始化的时候构建,一般都从sys.path中获得。如果被载入的模块是一个子包,那么该path entries就会从父包的__path__构建。
好了,现在知道python是如何一步一步运行到finder的,如果finder找到了目标模块,那么就会返回一个module spec,可是它与loader似乎没有什么关系啊?loader究竟是如何得到调用的? 先来看看module spec包含什么: # module spec
其中,重点关注__name__,__loader__。 __name__:模块名。 __loader__:用来加载该模块的loader。 原来finder将对应的模块loader放在module spec中一起返回了。 当python知道了一个模块的module spec的时候,就会紧接着调用对应的loader。 #loader loader提供了关键的模块加载功能,它会创建模块对象,然后将该module放入到缓存sys.modules中,接着执行该模块代码。 小结: 一次__import__可能会包含多次的模块查找(载入的模块是子模块的话,需要先载入父包),这就意味着会有多次的finder和loader的执行,当这些finder和对应的loader都执行完毕之后,__import__最后根据情况返回一个模块对象。 其他细节#子模块 当使用任意机制 (例如? |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/15 16:01:03- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |