0%

[python源码分析] import模块

import模块#

import xxx#

定义test.py

1
import _locale

用dis模块编译一下

1
2
3
4
5
6
7
C:\Users\liuw\Desktop> python.exe -m dis .\test.py
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (None)
4 IMPORT_NAME 0 (_locale)
6 STORE_NAME 0 (_locale)
8 LOAD_CONST 1 (None)
10 RETURN_VALUE

想象一下,当前 有两个或多个线程正在导入相同的random模块,CPython如何处理这种情况?

  1. 操作码IMPORT_NAME将检查导入的名称是否在sys.module中,如果是,则返回 sys.module中的内容
  2. 尝试获取锁 **_imp**
  3. 使用模块名称获取_module_locks中的锁对象,如有必要,在position 1中创建
  4. 尝试在第3步(position 2)中获取锁定对象
  5. 释放锁 **_imp**(position 3)
  6. 检查要导入的名称是否在sys.module中,如果是,则释放_module_locks中的锁对象并返回sys.module中的内容(position 4)
  7. 对于 sys.meta_path中的 finder,如果finder可以加载模块名称,请释放_module_locks中的锁对象并返回已加载的内容
  8. raise an error 在position 1,只有拥有_imp的线程才能修改_module_locks,当前线程将检查要导入的模块名称是否在_module_locks中,如果不是,则在_module_locks中插入新的锁定对象
在position 3,释放_imp锁,如果有其他线程导入其他模块,则它可以获得_imp锁并继续执行该过程,如果有其他线程导入同一模块,即使它成功获取了_imp,也将失败在获取_module_locks中的锁时,因为先前的线程持有该锁

在position 4,当前线程再次检查 sys.modules中的缓存

在position 5,它将在每个 finder调用之前获取 **_imp**锁。在函数调用之后查找并释放它

import xxx as x#

定义test.py
1
import demo as d
用dis模块编译一下
1
2
3
4
5
6
7
C:\Users\liuw\Desktop> python.exe -m dis .\test.py
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

可以看出,这里仅仅是 将模块的名字变为 d 了.因此,这个 import 语句变体其实等价于:

1
2
3
import demo
d = demo
del demo

from import#

定义test.py

1
from demo import value
同样用 dis 对语句进行反编译,我们得到以下字节码:
1
2
3
4
5
6
7
8
9
C:\Users\liuw\Desktop> python.exe -m dis .\test.py
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 指令: 从栈顶模块中取出指定名字,并保存于栈顶。

注意到,value 以 元组 的形式保存于栈顶,IMPORT_NAME 指令如果发现 value 为 demo 模块的子模块,将同时加载 value 子模块。此外,IMPORT_FROM 与 STORE_NAME 这两个指令相互配合,从模块中取出给定名字并保存。

仅仅加载了模块下的一部分

from import as#

1
2
3
4
5
6
7
8
9
10
//from demo import func as f
C:\Users\liuw\Desktop> python.exe -m dis .\test.py
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (('func',))
4 IMPORT_NAME 0 (demo)
6 IMPORT_FROM 1 (func)
8 STORE_NAME 2 (f) # 存储名字的时候存储的是 f
10 POP_TOP
12 LOAD_CONST 2 (None)
14 RETURN_VALUE

reference#

  1. module-github
  2. Python 源码深度剖析/22 模块动态加载, import 背后哪些事儿