9

我想知道以下行为是预期的还是错误的。我正在使用 CPython2.7

创建一个文件 x.py

def funcA():
    print "funcA of x.py"
def funcB():
    print "funcB of x.py"

创建一个文件 y.py

def funcB():
    print "funcB of y.py"

创建文件 test.py

import sys, imp
# load x.py as fff
m = imp.load_source('fff', 'x.py')
print dir(m)
print sys.modules.get('fff')
# load y.py as fff
m = imp.load_source('fff', 'y.py')
print dir(m)    
print sys.modules.get('fff')

# import and exec func
import fff
fff.funcA()
fff.funcB()
print dir(fff)

结果

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'funcA', 'funcB']
<module 'fff' from 'x.py'>
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'funcA', 'funcB']
<module 'fff' from 'y.py'>
funcA of x.py
funcB of y.py
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'funcA', 'funcB']

我的期望是第二个imp.load_source将完全用 y.py 替换模块 x.py。事实上sys.modules.get('fff')显示<module 'fff' from 'y.py'>,但生成的模块就像 x.py 和 y.py 的混合,后者具有优先级。

这是预期的还是错误的?

编辑:我的测试代码有错字。更新了结果。

4

1 回答 1

15

这是预期的行为。
http://docs.python.org/2/library/imp.html

imp.load_source(名称,路径名[,文件])

加载并初始化作为 Python 源文件实现的模块并返回其模块对象。如果模块已经初始化,它将再次初始化。name 参数用于创建或访问模块对象。

由于您的第二个模块与第一个模块具有相同的名称,因此它不会替换第一个模块,而是会合并到第一个模块中。

源代码给了我们同样的答案。
imp是一个内置模块,在import.c中定义。
我们来看看定义load_source

static PyObject *
load_source_module(char *name, char *pathname, FILE *fp)
{
    ......
    m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, pathname);
    Py_DECREF(co);

    return m;
}

它只是PyImport_ExecCodeModuleEx.

PyObject *
PyImport_ExecCodeModuleEx(char *name, PyObject *co, char *pathname)
{
    PyObject *modules = PyImport_GetModuleDict();
    PyObject *m, *d, *v;

    m = PyImport_AddModule(name);
    ......
    d = PyModule_GetDict(m);
    ......
    v = PyEval_EvalCode((PyCodeObject *)co, d, d);
    ......
}

现在,我们只需要专注于PyImport_AddModule. Python 使用它来获取模块对象。您解析的源文件将被放入此模块对象中。

PyObject *
PyImport_AddModule(const char *name)
{
    PyObject *modules = PyImport_GetModuleDict();
    PyObject *m;

    if ((m = PyDict_GetItemString(modules, name)) != NULL &&
        PyModule_Check(m))
        return m;
    m = PyModule_New(name);
    if (m == NULL)
        return NULL;
    if (PyDict_SetItemString(modules, name, m) != 0) {
        Py_DECREF(m);
        return NULL;
    }
    Py_DECREF(m); /* Yes, it still exists, in modules! */

    return m;
}

最后,我们找到了答案。给定 a name,如果某个模块已经具有 this name,即 ,name in sys.modulesPython 不会创建新模块,而是会重用该模块。

于 2013-02-26T07:44:21.747 回答