2013-02-26 7 views
9

以下の動作が予想されるか、バグかどうかを知りたいと思います。私が作成します作成したファイルtest.pyモジュールをimp.load_sourceでモジュールをロードして同じ名前でモジュールの合併を行います

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) 

をファイルy.py

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

をファイルx.py

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

をCPython2.7

を使用しています結果

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

2番目のimp.load_sourceは、x.pyモジュールをy.pyと完全に置き換える予定でした。実際にはsys.modules.get('fff')には<module 'fff' from 'y.py'>が表示されますが、結果として得られるモジュールはx.pyとy.pyの組み合わせのようなもので、後者が優先されます。

これは問題なのですか、それともバグですか?

EDIT:テストコードに誤字がありました。結果を更新しました。

答えて

13

これは予期された現象です。
http://docs.python.org/2/library/imp.html

imp.load_source(名、パス名[ファイル])

ロードを参照して、Pythonのソースファイルとして実装モジュールを初期化し、そのモジュールオブジェクトを返します。モジュールが既に初期化されている場合、モジュールは再び初期化されます。 name引数は、アクセスモジュールオブジェクトを作成するために使用されます。

あなたの第二のモジュールが最初のモジュールと同じ名前を持っているので、それは最初のものを置き換えることはありませんが、最初の1にマージされます。

ソースコードは私たちに同じ答えを与えます。
impは、内蔵モジュールで、import.cで定義されています。
はのは、それはPyImport_ExecCodeModuleExための単なるラッパーです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; 
} 

の定義を見てみましょう。

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

大変ありがとうございます。私はこれの良いユースケースが何か疑問に思います。この文書は、合併についてより明白になるかもしれない。 'load_module'や' reload'のような同じモジュール内の他の関数は、はるかに良いドキュメントを持っています。 –

+2

モジュールがマージされずに置き換えられる方法はありますか? – foobar

+1

はい、常に、imp.load_sourceの前に 'del sys.modules ['モジュール名']'があります。 –

関連する問題