2013-02-26 15 views
9

Vorrei sapere se è previsto il seguente comportamento o un bug. Sto usando CPython2.7caricamento moduli da imp.load_source con lo stesso nome risultante dalla fusione dei moduli

Creare un file x.py

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

Creare un file y.py

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

Creare un file 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) 

Il risultato

['__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'] 

La mia aspettativa era che il secondo imp.load_source sostituisse completamente il modulo x.py con y.py. Infatti, sys.modules.get('fff') mostra <module 'fff' from 'y.py'> ma il modulo risultante era come un mix di x.py e y.py e quest'ultimo ha la precedenza.

È previsto o un errore?

EDIT: il mio codice di prova ha avuto un refuso. aggiornato il risultato.

risposta

13

Questo è un comportamento previsto.
Vedi http://docs.python.org/2/library/imp.html

imp.load_source (nome, percorso [, file])

Carica e inizializza un modulo implementato come un file sorgente Python e restituisce il suo modulo oggetto. Se il modulo è già stato inizializzato, verrà nuovamente inizializzato. L'argomento nome viene utilizzato per creare o l'accesso all'oggetto.

Dal momento che il secondo modulo ha lo stesso nome del primo modulo, che non sostituirà il primo, ma sarà fuse in primo.

Il codice sorgente ci dà la stessa risposta.
imp è un modulo integrato, definito in import.c. sguardo
Let presso alla definizione di 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; 
} 

E 'solo un wrapper per 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); 
    ...... 
} 

Ora, abbiamo solo bisogno di concentrarsi su PyImport_AddModule. Python lo usa per ottenere un oggetto modulo. Il tuo file sorgente analizzato verrà inserito in questo oggetto modulo.

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; 
} 

Infine, troviamo la risposta. Dato un name, se alcuni moduli hanno già questo name, ovvero name in sys.modules, Python non creerà un nuovo modulo, ma riutilizzerà quel modulo.

+3

Grazie mille per aver guardato fino in import.c. Mi chiedo qual è il caso di utilizzo corretto di questo.Il documento potrebbe essere più esplicito sulla fusione. Altre funzioni nello stesso modulo come 'load_module' e' reload' hanno una documentazione molto migliore. –

+2

Esiste un modo in cui i moduli non vengono uniti ma sostituiti? – foobar

+1

sì, sempre 'del sys.modules ['nome del modulo']' prima di imp.load_source. –

Problemi correlati