import模块#
import xxx#
定义test.py
1
import _locale
用dis模块编译一下
1
2
3
4
5
6
7C:\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如何处理这种情况?
- 操作码IMPORT_NAME将检查导入的名称是否在sys.module中,如果是,则返回 sys.module中的内容
- 尝试获取锁 **_imp**
- 使用模块名称获取_module_locks中的锁对象,如有必要,在position 1中创建
- 尝试在第3步(position 2)中获取锁定对象
- 释放锁 **_imp**(position 3)
- 检查要导入的名称是否在sys.module中,如果是,则释放_module_locks中的锁对象并返回sys.module中的内容(position 4)
- 对于 sys.meta_path中的 finder,如果finder可以加载模块名称,请释放_module_locks中的锁对象并返回已加载的内容
- raise an error 在position 1,只有拥有_imp的线程才能修改_module_locks,当前线程将检查要导入的模块名称是否在_module_locks中,如果不是,则在_module_locks中插入新的锁定对象

在position 4,当前线程再次检查 sys.modules中的缓存
在position 5,它将在每个 finder调用之前获取 **_imp**锁。在函数调用之后查找并释放它
import xxx as x#
定义test.py1 | import demo as d |
1 | C:\Users\liuw\Desktop> python.exe -m dis .\test.py |

可以看出,这里仅仅是 将模块的名字变为 d
了.因此,这个 import 语句变体其实等价于:
1
2
3import demo
d = demo
del demo
from import#
定义test.py
1
from demo import value
1
2
3
4
5
6
7
8
9C:\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

注意到,value 以 元组 的形式保存于栈顶,IMPORT_NAME 指令如果发现 value 为 demo 模块的子模块,将同时加载 value 子模块。此外,IMPORT_FROM 与 STORE_NAME 这两个指令相互配合,从模块中取出给定名字并保存。
仅仅加载了模块下的一部分
from import as#
1 | //from demo import func as f |