我们知道 import 关键字用于导入模块,例如:
|
|
此外,import 语句还有其他几种变体,例如:
|
|
那么,Python 模块加载的过程是怎样的呢?不同 import 语句都有哪些异同,背后具体执行了什么动作?Python 又是如何找到被导入模块的呢?带着这些问题,我们开始学习 Python 的 模块加载 机制。
字节码
透过字节码,我们可以洞悉 Python 执行语句的全部秘密。因此,我们从研究 import 语句字节码入手,逐步深入研究模块的加载过程。
import
以最基本的 import 语句为例:
|
|
借助 dis 模块,我们将这个语句反编译,得到以下字节码:
|
|
我们重点关注前 4 条字节码,看它们在 Python 虚拟机中是如何执行的:
- 前 2 条字节码执行完毕后,0 以及 None 这两个常量被加载到栈中;
- 顾名思义,IMPORT_NAME 指令负责加载模块,模块名由操作数指定,其他参数从栈上取;模块加载完毕后,模块对象便保存在栈顶;
- 最后,STORE_NAME 指令从栈顶取出模块对象并保存到局部名字空间中;
至此,Python 模块动态加载的秘密已经浮出水面了。在字节码层面,IMPORT_NAME 负责加载模块, 模块名 由操作数指定,其他参数来源于 运行栈 。虽然 IMPORT_NAME 指令还没来得及研究,成就感也是满满的呢!
import as
开始研究 IMPORT_NAME 指令实现细节前,一鼓作气将其他几种 import 语句变体拿下。先研究 impot as 语句:
|
|
同样用 dis 对语句进行反编译,我们得到以下字节码:
|
|
这段字节码跟前一段几乎一模一样,区别只是 STORE_NAME 指令,它用换个名字来保存被加载模块:
因此,这个 import 语句变体其实等价于:
|
|
from import
现在再接再厉,拿下 from import 语句:
|
|
同样用 dis 对语句进行反编译,我们得到以下字节码:
|
|
我们看到一个新面孔 —— IMPORT_FROM 指令。那么这个指令具体是什么作用呢?上述字节码你看懂了吗?Python 模块动态加载机制 又是如何运作的呢?点击“阅读原文”,获取更多详情!
【Python源码剖析】系列文章首发于公众号【小菜学编程】,敬请关注: