模块动态加载机制

  1. Advanced Python(某一期Google TechTalks的话题)上提到import指令本质是个语法糖,import sys等价于sys = import(“sys”)。解析import sys的bytecode可以看到四个指令(参数略): LOAD_CONST LOAD_CONST IMPORT_NAME STORE_NAME

IMPORT_NAME把sys模块导入并保存到栈上,STORE_NAME把这个指针当作普通对象保存在sys这个变量中。

2. IMPORT_NAME指令行为分析 将参数打包并用PyEval_CallObject()这个统一调用接口运行import方法,bltinmodule.c中的builtinimport函数包装了这个功能。help(import)显示的import方法的参数列表 import(…) import(name, globals={}, locals={}, fromlist=[], level=-1) -> module

对应于builtinimport中调用的另一层函数封装

PyObject *
PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)

这个函数调用了真正干活的函数,import.c中的

static PyObject *
import_module_level(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)

3. import_module_level函数 首先调用get_parent()得到import发生时的package对象,接下来多次调用load_next依次得到各级的包名,跟踪了下import xml.dom.minidom的行为,发现name的值依次是”dom.minidom”, “minidom”。最后根据import的形式是from … import …还是import …返回不同的处理结果。

4. import_submodule函数 load_next函数中调用这个函数进行模块的查找和载入。主要分三步:find_module, load_module, add_submodule。

5. from … import … ensure_fromlist处理了这个情况。另外对于from … import *的情况,会从module文件中读入一个all对象,从中知道module公开的符号信息。

几个以后可能用到的函数:

[import.c]
/* Parse a source file and return the corresponding code object */
static PyCodeObject * parse_source_module(const char *pathname, FILE *fp);

/* Execute a code object in a module and return the module object
 * WITH INCREMENTED REFERENCE COUNT.  If an error occurs, name is
 * removed from sys.modules, to avoid leaving damaged module objects
 * in sys.modules.  The caller may wish to restore the original
 * module object (if any) in this case; PyImport_ReloadModule is an
 * example.
 */
PyObject * PyImport_ExecCodeModule(char *name, PyObject *co);