2013-02-26 72 views
9

我想知道以下行为是否预期或错误。我使用CPython2.7通过imp.load_source以相同的名称加载模块导致模块合并

创建一个文件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的混合,后者具有优先级。

这是预期或错误?

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

回答

13

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

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

加载和初始化为Python源文件中实现的模块,并且返回其模块对象。如果模块已经初始化,它将被重新初始化。名称参数用于创建或访问模块对象的或

因为你的第二个模块具有相同的名称作为第一个模块,它不会取代第一个,但将被合并到第一个。

源代码给了我们相同的答案。
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; 
} 

最后,我们找到了答案。给定一个name,如果某个模块已经有这个name,即name in sys.modules,Python不会创建一个新模块,但会重用该模块。

+3

非常感谢您一直注意import.c。我想知道这有什么好的用例。该文件可能对合并更加明确。相同模块中的其他函数,如'load_module'和'reload'具有更好的文档。 – 2013-02-26 17:41:03

+2

有没有一种方式,模块不合并,但被替换? – foobar 2013-12-29 20:51:04

+1

是的,在imp.load_source之前总是有'del sys.modules ['module name']。 – 2014-06-17 00:19:42