理解Python Import机制可以对Python的Package结构进行更深刻的理解。
1.__import__函数
__import__函数是import关键字的内部实现。
static PyObject *
builtin___import__(PyObject *self, PyObject *args, PyObject *kwds) {
static char *kwlist[] = {"name", "globals", "locals", "fromlist",
"level", 0};
char *name;
PyObject *globals = NULL;
PyObject *locals = NULL;
PyObject *fromlist = NULL;
int level = -1;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OOOi:__import__",
kwlist, &name, &globals, &locals, &fromlist, &level))
return NULL;
return PyImport_ImportModuleLevel(name, globals, locals,
fromlist, level);
}
builtin___import__函数内部很简单,主要是提取kwlist列出的几个参数,然后就进入了PyImport_ImportModuleLevel方法内。PyImport_ImportModuleLevel函数只是对即将进行的操作进行加锁,然后调用方法import_module_level。
static PyObject *
import_module_level(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level) {
// [1] 获取import发生时的package
parent = get_parent(globals, buf, &buflen, level);
// [2] 解析module的路径结构,依次加载每一个package/module
head = load_next(parent, level < 0 ? Py_None : parent, &name, buf,
&buflen);
if (head == NULL)
goto error_exit;
tail = head;
Py_INCREF(tail);
while (name) {
next = load_next(tail, tail, &name, buf, &buflen);
Py_DECREF(tail);
if (next == NULL) {
Py_DECREF(head);
goto error_exit;
}
tail = next;
}
// import xx.xx.xx 形式
if (fromlist == NULL) {
Py_DECREF(tail);
PyMem_FREE(buf);
return head;
}
Py_DECREF(head);
// from xx.xx import xx 形式
if (!ensure_fromlist(tail, fromlist, buf, buflen, 0)) {
Py_DECREF(tail);
goto error_exit;
}
PyMem_FREE(buf);
return tail;
}
get_parent方法用于获取当前import动作所在的package。load_next可以认为是在导入name中声明的每一个模块,每次调用load_next都会返回本次导入的模块的PyObject,并且修改name的信息,将上次导入的模块的名称删除。
2.get_parent函数
static PyObject *
get_parent(PyObject *globals, char *buf, Py_ssize_t *p_buflen, int level)
{
......
// 获取__package__信息,就是当前import的环境下,其所在的package叫什么名字
pkgname = PyDict_GetItem(globals, pkgstr);
if ((pkgname != NULL) && (pkgname != Py_None)) {
......
// 如果package是存在的,那么我们直接使用它
// 将package的内容直接设置到buf里面
strcpy(buf, PyString_AS_STRING(pkgname));
} else {
// __package__并没有设置,我们通过其他方法找出来
// 先获得我们import的环境的__name__变量
modname = PyDict_GetItem(globals, namestr);
if (modname == NULL || !PyString_Check(modname))
return Py_None;
// 再获得__path__变量,需要知道的一点是在__init__.py
// 内部__path__变量是存在的,但其他module内部__path__
// 变量是为None的
modpath = PyDict_GetItem(globals, pathstr);
if (modpath != NULL) {
......
// __path__已经被设置了,那么__name__的值就是package的名称
// 将package的内容直接设置到buf里面
strcpy(buf, PyString_AS_STRING(modname));
......
} else {
// __package__名称为空,那么import的动作是在某个module内部的
char *start = PyString_AS_STRING(modname);
// lastdot就是当前module所在的package的名称
char *lastdot = strrchr(start, '.');
size_t len;
int error;
if (lastdot == NULL && level > 0) {
PyErr_SetString(PyExc_ValueError,
"Attempted relative import in non-package");
return NULL;
}
if (lastdot == NULL) {
// 我们import的环境是最上层的环境
// 比如说得命令行环境
// 程序最初的运行文件等
error = PyDict_SetItem(globals, pkgstr, Py_None);
if (error) {
PyErr_SetString(PyExc_ValueError,
"Could not set __package__");
return NULL;
}
return Py_None;
}
len = lastdot - start;
// 将__package__的信息设置到buf里面去
strncpy(buf, start, len);
......
}
}
// level > 0 的情况很多时候都是
// from ... import a.b
// 上面的level=3
while (--level > 0) {
char *dot = strrchr(buf, '.');
if (dot == NULL) {
PyErr_SetString(PyExc_ValueError,
"Attempted relative import beyond "
"toplevel package");
return NULL;
}
*dot = '\0';
}
*p_buflen = strlen(buf);
modules = PyImport_GetModuleDict();
// 从sys.modules中获取对应的package
parent = PyDict_GetItemString(modules, buf);
return parent;
}
3.load_next函数
static PyObject *
load_next(PyObject *mod, PyObject *altmod, char **p_name, char *buf,
Py_ssize_t *p_buflen)
{
... ...
// p_name指向被截取后的路径名称
if (dot == NULL) {
*p_name = NULL;
len = strlen(name);
}
else {
*p_name = dot+1;
len = dot-name;
}
... ...
// 修改buf的内容,将截取的模块内容拷贝到buf后
p = buf + *p_buflen;
if (p != buf)
*p++ = '.';
if (p+len-buf >= MAXPATHLEN) {
PyErr_SetString(PyExc_ValueError,
"Module name too long");
return NULL;
}
strncpy(p, name, len);
p[len] = '\0';
*p_buflen = p+len-buf;
// import模块
result = import_submodule(mod, p, buf);
if (result == Py_None && altmod != mod) {
//如果没有import成功,那么我们从默认路径开始import
Py_DECREF(result);
/* Here, altmod must be None and mod must not be None */
result = import_submodule(altmod, p, p);
if (result != NULL && result != Py_None) {
if (mark_miss(buf) != 0) {
Py_DECREF(result);
return NULL;
}
strncpy(buf, name, len);
buf[len] = '\0';
*p_buflen = len;
}
}
... ...
return result;
}
4.领悟
- import是不能import进module内部的类的,比如说module A中实现了类A,那么import A_p.A.A会有问题,需要改为from A_p.A import A
- 对应from A_p.A import a形式,我们可以看成__import__(name = “A_p.A”, from_list = [“a”]),函数返回的是module A