# 模块加载和搜索
# 模块加载机制
Python内部会用一个dict对象保存所有已经加载过的模块
>>> import sys >>> for name, module in sys.modules.items(): ... print(name) ... sys builtins # ...当Python加载模块前,会先检查
sys.modules,如果发现目标模块已经加载过,则直接将其返回。因此无论一个模块被多少个
import语句导入,第一次加载后便不再重复加载。同时还有一个
sys.path列表维护模块搜索的路径,其中第一个元素是一个空字符串表示main包所在的目录-即入口文件所在的目录当目标模块找到后,Python对代码进行编程生成
PyCodeObject,如果存在pyc文件则可以省略编译步骤转而直接从pyc文件中加载PyCodeObject。# 读取模块代码 text = read('demo.py') # 编译模块代码 code = compile(text, 'demo.py', 'exec')进而Python创建一个全新的
PyModuleObject模块对象-只提供模块名和模块文档信息。demo = module('demo', 'A test module')然后Python执行模块代码对象,完成模块初始化
exec(code, demo.__dict__, demo.__dict__)最后将模块对象保存到
sys.modules避免重复加载import sys sys.modules['demo'] = demo
# 模块加载方式字节码
# import
import demo
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
# import as
import demo as d
# 等价于
import demo
d = demo
del demo
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
# from import
from demo import value
# 等价于
import demo
value = demo.value
del demo
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
# from import as
from demo import value as v
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (('value',))
4 IMPORT_NAME 0 (demo)
6 IMPORT_FROM 1 (value)
8 STORE_NAME 2 (v)
10 POP_TOP
12 LOAD_CONST 2 (None)
14 RETURN_VALUE
# 模块加载流程
# IMPORT_NAME
TARGET(IMPORT_NAME) {
PyObject *name = GETITEM(names, oparg); // 需要加载的模块名
PyObject *fromlist = POP(); // 参数列表-即需要记载的潜在子模块
PyObject *level = TOP();
PyObject *res;
res = import_name(f, name, fromlist, level);
Py_DECREF(level);
Py_DECREF(fromlist);
SET_TOP(res); // 释放参数并将加载到的模块对象保存到栈顶
if (res == NULL)
goto error;
DISPATCH();
}
# PyImport_ImportModuleLevelObject
PyObject *
PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
PyObject *locals, PyObject *fromlist,
int level);
// 需要传入globals是因为需要据此去计算模块绝对路径(如果使用了相对路径)-模块名存在globals名字空间中