Py_InitModule 复制名称但不复制函数指针?
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
调用重新注册它。但是同时,改名字就没有这个行为了。
到目前为止,我非常确定 PyMethodDef
需要在 python 代码尝试调用方法时存活(即不能是 stack/local 变量),但只使用了函数指针本身。
这是故意行为还是疏忽?该文档没有提及我能找到的有关 PyMethodDef 生命周期的任何信息。
您看到的不一致是由于函数代码(即函数本身的 属性)与从模块中调用它的名称(即 属性 模块(在其字典中输入)。虽然函数名称也存储在函数对象中,但它仅用于 repr
而不是函数的基础 属性。
这是有意为之的,因为它允许在不同的地方以不同的名称使用相同的函数对象——如果函数存储在容器中,甚至可以不用名称。如果仅通过更改函数的 属性 就可以 "rename" 这是不可能的。
可以使用常规 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
和相关函数被设计为使用静态分配的结构来调用,创建副本将浪费 space.不复制它们解释了为什么更改 ml_meth
中的实际 C 回调会更改 Python 可调用对象。
当我使用 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 代码尝试调用方法时存活(即不能是 stack/local 变量),但只使用了函数指针本身。
这是故意行为还是疏忽?该文档没有提及我能找到的有关 PyMethodDef 生命周期的任何信息。
您看到的不一致是由于函数代码(即函数本身的 属性)与从模块中调用它的名称(即 属性 模块(在其字典中输入)。虽然函数名称也存储在函数对象中,但它仅用于 repr
而不是函数的基础 属性。
这是有意为之的,因为它允许在不同的地方以不同的名称使用相同的函数对象——如果函数存储在容器中,甚至可以不用名称。如果仅通过更改函数的 属性 就可以 "rename" 这是不可能的。
可以使用常规 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
和相关函数被设计为使用静态分配的结构来调用,创建副本将浪费 space.不复制它们解释了为什么更改 ml_meth
中的实际 C 回调会更改 Python 可调用对象。