模块动态加载,import 背后那些事儿

路漫漫其修远兮,吾将上下而求索。

—— 战国·屈原·《离骚》

我们知道 import 关键字用于导入模块,例如:

1
import demo

此外,import 语句还有其他几种变体,例如:

1
2
3
4
import demo as d

from demo import value
from demo import value as v

那么,Python 模块加载的过程是怎样的呢?不同 import 语句都有哪些异同,背后具体执行了什么动作?Python 又是如何找到被导入模块的呢?带着这些问题,我们开始学习 Python模块加载 机制。

字节码

透过字节码,我们可以洞悉 Python 执行语句的全部秘密。因此,我们从研究 import 语句字节码入手,逐步深入研究模块的加载过程。

import

以最基本的 import 语句为例:

1
import demo

借助 dis 模块,我们将这个语句反编译,得到以下字节码:

1
2
3
4
5
6
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (None)
              4 IMPORT_NAME              0 (demo)
              6 STORE_NAME               0 (demo)
              8 LOAD_CONST               1 (None)
             10 RETURN_VALUE

我们重点关注前 4 条字节码,看它们在 Python 虚拟机中是如何执行的:

  1. 2 条字节码执行完毕后,0 以及 None 这两个常量被加载到栈中;
  2. 顾名思义,IMPORT_NAME 指令负责加载模块,模块名由操作数指定,其他参数从栈上取;模块加载完毕后,模块对象便保存在栈顶;
  3. 最后,STORE_NAME 指令从栈顶取出模块对象并保存到局部名字空间中;

至此,Python 模块动态加载的秘密已经浮出水面了。在字节码层面,IMPORT_NAME 负责加载模块, 模块名 由操作数指定,其他参数来源于 运行栈 。虽然 IMPORT_NAME 指令还没来得及研究,成就感也是满满的呢!

import as

开始研究 IMPORT_NAME 指令实现细节前,一鼓作气将其他几种 import 语句变体拿下。先研究 impot as 语句:

1
import demo as d

同样用 dis 对语句进行反编译,我们得到以下字节码:

1
2
3
4
5
6
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (None)
              4 IMPORT_NAME              0 (demo)
              6 STORE_NAME               1 (d)
              8 LOAD_CONST               1 (None)
             10 RETURN_VALUE

这段字节码跟前一段几乎一模一样,区别只是 STORE_NAME 指令,它用换个名字来保存被加载模块:

因此,这个 import 语句变体其实等价于:

1
2
3
import demo
d = demo
del demo

from import

现在再接再厉,拿下 from import 语句:

1
from demo import value

同样用 dis 对语句进行反编译,我们得到以下字节码:

1
2
3
4
5
6
7
8
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (('value',))
              4 IMPORT_NAME              0 (demo)
              6 IMPORT_FROM              1 (value)
              8 STORE_NAME               1 (value)
             10 POP_TOP
             12 LOAD_CONST               2 (None)
             14 RETURN_VALUE

我们看到一个新面孔 —— IMPORT_FROM 指令。那么这个指令具体是什么作用呢?上述字节码你看懂了吗?Python 模块动态加载机制 又是如何运作的呢?点击“阅读原文”,获取更多详情!

【Python源码剖析】系列文章首发于公众号【小菜学编程】,敬请关注:

【Python源码剖析】系列文章首发于公众号【小菜学编程】,敬请关注: