2013-02-26 69 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

相關問題