[英]Py_InitModule copies the name but not the function pointer?
當我使用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
調用將其重新注冊也是Py_InitModule
。 但是同時,更改名稱沒有此行為。
到目前為止,我很確定PyMethodDef
必須存在,只要python代碼嘗試調用方法(即不能是堆棧/局部變量),但是僅使用函數指針本身。
這是故意的行為還是某種疏忽? 該文檔沒有提及我能找到的有關PyMethodDef生存期的任何內容。
您看到的不一致是由函數代碼(即函數本身的屬性)與從模塊調用模塊的名稱(即模塊的屬性)(其字典中的鍵)之間的差異引起的。 雖然函數的名稱也存儲在函數對象中,但它僅用於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
和相關函數被設計為使用靜態分配的結構來調用,因此創建其副本將浪費空間。 不復制它們說明了為什么更改ml_meth
的實際C回調ml_meth
更改Python可調用項。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.