Python C API:通过引用传递参数调用Python函数
我正在用C语言写一个Python模块,我需要让它调用一个Python函数,并且其中一个参数是通过“引用”传递的。最终的结果应该是,Python函数对这个参数的修改能够反映到原来的C变量上。
int *resume; // this int is actually passed in to this body of code
PyObject *resumeInt; // which was originally a C callback func for libnids, but
PyObject *ret; // I removed/rewrote most of this code for clarity
resumeInt = Py_BuildValue("i",-1);
ret = PyObject_CallFunction(tcpResumeFunc, "(O)", resumeInt);
*resume = PyInt_AsLong(resumeInt);
Py_DECREF(ret);
Py_DECREF(resumeInt);
为了测试,我让tcpResumeFunc这个Python函数把传入的整数改成5。然而,当我在这段代码的最后打印出*resume时,它仍然保持最初的-1值。我知道我对API的工作原理有些误解。有什么建议吗?
1 个回答
我觉得你对Python中变量的工作方式有些误解。在Python中,变量并不是直接包含值,而是指向这些值。就像在Java中一样,不过Python没有“基本类型”(连int
都没有)。给变量赋值并不会覆盖旧的值,而是让这个变量指向新的值,同时不再指向旧的值(如果没有其他地方再指向旧值,它可能会被垃圾回收)。
那么为什么不直接返回(并使用)一个值呢?因为Python的函数总是会返回一些东西,即使只是隐式返回None
。
编辑:通过一个例子来说明,因为评论回复太长了。
从C语言中,我们调用PyObject_CallFunction,这个函数会把一个PyObject*传递给Python函数。在Python中,函数的参数(我们称它为spam
)现在指向了那个PyObject。
当我们在函数中写spam += 6
时,这其实和spam = spam + 6
是一样的。字节码解释器会获取表示值6的PyObject,运行一个特殊的字节码来检查这两个对象所代表的值,把它们相加,并创建一个新的PyObject(或者从缓存中获取一个)来表示这个和。
然后spam
会重新指向这个新对象;但是spam
这个变量引用是在Python这边的内存中,它和C这边的PyObject*并不是同一个东西。
因此,resumeInt
并不会指向新创建或获取的PyObject。
它的行为实际上和从Python调用Python函数时是完全一样的。试试看:
def fry(spam):
spam += 1
eggs = 3
fry(eggs)
eggs # still 3!
之所以会这样,是因为重新绑定spam
并不会影响eggs
,因为eggs
是一个独立的变量。我们并不是通过引用传递,而是通过值传递一个引用。就像在Java中一样。