Daily Notes on Python[11.17-11.23]
模块动态加载机制
- 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中的builtin__import__函数包装了这个功能。help(import)显示的__import__方法的参数列表 import(…) import(name, globals={}, locals={}, fromlist=[], level=-1) -> module
对应于builtin__import__中调用的另一层函数封装
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);