当我使用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
调用重新注册它。但与此同时,更改名称并没有这种行为。
到目前为止,我很确定只要python代码尝试调用方法(即不能是堆栈/局部变量),PyMethodDef
就需要生存,但只有函数指针本身被使用。
这是故意行为,还是一些疏忽?文档中没有提到我能找到的有关PyMethodDef生命周期的任何内容。
答案 0 :(得分: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
和相关函数被设计为使用静态分配的结构调用,创建副本会浪费空间。不复制它们解释了为什么更改ml_meth
中的实际C回调会更改Python可调用。