2016-08-28 17 views
1

Py_InitModuleを使用してコールバックを登録し、後で構造内の関数ポインタを新しい関数を指すように変更すると、新しい関数が呼び出されます。しかし、名前を変更すると、新しい名前は認識されません。Py_InitModuleは名前をコピーしますが、関数ポインタはコピーしませんか?

#include <Python.h> 

PyObject* foo1(PyObject *self, PyObject *args) 
{ 
    printf("foo1\n"); 
    Py_RETURN_NONE; 
} 

PyObject* foo2(PyObject *self, PyObject *args) 
{ 
    printf("foo2\n"); 
    Py_RETURN_NONE; 
} 

int main() 
{ 
    PyMethodDef methods[] = { 
     { "foo", foo1, METH_VARARGS, "foo" }, 
     { 0, 0, 0, 0 } 
    }; 

    Py_Initialize(); 
    Py_InitModule("foo", methods); 
    PyRun_SimpleString("import foo\n"); 
    PyRun_SimpleString("foo.foo()\n"); 
    methods[0].ml_meth = foo2; 
    PyRun_SimpleString("foo.foo()\n"); 
    methods[0].ml_name = "foo2"; 
    PyRun_SimpleString("foo.foo()\n"); 
    PyRun_SimpleString("foo.foo2()\n"); 
    return 0; 
} 

これは、次のような出力が得られます。

foo1 
foo2 
foo2 
Traceback (most recent call last): 
    File "", line 1, in 
AttributeError: 'module' object has no attribute 'foo2'

これは非常に一貫性のない行動です。 PyMethodDef methodsのスタック変数を使用したときに最初に遭遇しました。この変数は、変数が有効範​​囲外になったときにプログラムをクラッシュさせ、PythonからC++コールバックを呼び出そうとしました。だから私はポインタを変更すると実際には別のPy_InitModule呼び出しでそれを再登録していないにもかかわらず呼び出される関数を変更することをテストしました。しかし同時に、名前を変更してもこの動作はありません。

は、これまでのところ私は(すなわち/ローカル変数スタックすることはできません)PyMethodDefニーズがある限りPythonのコードは、メソッドを呼び出そうとするために生きていることをかなり確信しているが、唯一の機能は、自身が使用されているポインタ。

これは意図的な動作ですか、一部の見落としですか?ドキュメントにはPyMethodDefの存続期間については何も触れていません。

+1

これは予想されます。 'PyInit_Module'は何も初期化していないと思いましたか?それは後でそれらをチェックするのを避けるためにどの属性/メソッドが定義されているかをインタプリタに伝えます。関数の名前を動的に変更したい場合は、1)インタプリタからモジュールオブジェクトを取得します。2)PyObject_SetAttrを使って、モジュールオブジェクトの属性を設定するために、正確な名前を思い出しません。 – Bakuriu

+0

@Bakuriuありがとうございます。しかし、なぜ 'ml_math'フィールドもコピーされていませんか?つまり、後で呼び出す関数をホットスワップするのはなぜですか? – sashoalm

答えて

2

表示される不一致は、関数自体のプロパティである関数のコードと、モジュールのプロパティであるモジュールから呼び出された名前(そのdictのキー)。関数の名前も関数オブジェクトに格納されていますが、reprでのみ使用され、関数の基本的なプロパティではありません。

これは、機能がコンテナに格納されている場合は、名前の違う別の場所で同じ関数オブジェクトを使用することができます。これは、関数のプロパティを変更するだけで "名前を変更"することはできません。

一つは、このように、通常のPython関数を使用して、この同じ違いを発揮することができますコメントでのご質問については

>>> def add(a, b): return a + b 
... 
>>> def sub(a, b): return a - b 
... 
>>> add 
<function add at 0x7f9383127938> # the function has a name 
>>> add.__name__ = 'foo' 
>>> add       # the name is changed, but... 
<function foo at 0x7f9383127938> 
>>> foo       # the change doesn't affect the module 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'foo' is not defined 
>>> add.__code__ = sub.__code__ # we can change the code, though 
>>> add(2, 2) 
0 

:メソッドフィールドがコピーされませんPy_InitModuleおよび関連機能を使って呼び出されるように設計されているので、静的に割り当てられた構造体を作成し、そのコピーを作成することはスペースの無駄になります。それらをコピーしないと、実際のCコールバックをml_methに変更すると、Python呼び出し可能ファイルが変更される理由が説明されます。

関連する問題