在Python中将对象传递给C模块
我遇到了一个关于纯Python和C语言Python模块的问题。简单来说,我想知道如何在C模块中接收和处理Python对象。我的Python部分看起来是这样的。
#!/usr/bin/env python
import os, sys
from c_hello import *
class Hello:
busyHello = _sayhello_obj
class Man:
def __init__(self, name):
self.name = name
def getName(self):
return self.name
h = Hello()
h.busyHello( Man("John") )
在C语言中,有两个问题需要解决。首先,我该如何接收这个对象?其次,我该如何调用这个对象的方法?
static PyObject *
_sayhello_obj(PyObject *self, PyObject *args)
{
PyObject *obj;
// How can I fill obj?
char s[1024];
// How can I fill s, from obj.getName() ?
printf("Hello, %s\n", s);
return Py_None;
}
1 个回答
要从你的方法调用中提取参数,你需要查看一些文档,比如解析参数和构建值中的函数,比如PyArg_ParseTuple
。这个函数是用来处理位置参数的!如果你还需要处理关键字参数等,文档里还有其他的函数可以使用。
从PyArg_ParseTuple
返回的对象,它的引用计数不会增加。对于简单的C函数来说,你可能不需要担心这个问题。但如果你在和其他Python/C函数交互,或者在释放全局解释器锁(也就是允许多线程)时,你就需要非常仔细地考虑对象的所有权问题。
static PyObject *
_sayhello_obj(PyObject *self, PyObject *args)
{
PyObject *obj = NULL;
// How can I fill obj?
static char fmt_string = "O" // For "object"
int parse_result = PyArg_ParseTuple(args, fmt_string, &obj);
if(!parse_res)
{
// Don't worry about using PyErr_SetString, all the exception stuff should be
// done in PyArg_ParseTuple()
return NULL;
}
// Of course, at this point you need to do your own verification of whatever
// constraints might be on your argument.
如果你想在一个对象上调用方法,你需要使用PyObject_CallMethod
或者PyObject_CallMethodObjArgs
,具体取决于你是如何构造参数列表和方法名称的。还有,注意我在代码中提到的对象所有权问题!
稍微插一句,确保你不会在后面遇到麻烦:如果你只是想把字符串提取出来打印,直接获取对象引用并传给PyObject_Print
会更好。当然,也许你只是为了举例,或者你比我更清楚你想用这些数据做什么;)
char s[1024];
// How can I fill s, from obj.getName() ?
// Name of the method
static char method_name = "getName";
// No arguments? Score! We just need NULL here
char method_fmt_string = NULL;
PyObject *objname = PyObject_CallMethod(obj, obj_method, method_fmt_string);
// This is really important! What we have here now is a Python object with a newly
// incremented reference count! This means you own it, and are responsible for
// decrementing the ref count when you're done. See below.
// If there's a failure, we'll get NULL
if(objname == NULL)
{
// Again, this should just propagate the exception information
return NULL;
}
现在,在字符串/字节对象部分的具体对象层文档中有很多函数,你可以选择最适合你的那个。
但不要忘记这一点:
// Now that we're done with the object we obtained, decrement the reference count
Py_XDECREF(objname);
// You didn't mention whether you wanted to return a value from here, so let's just
// return the "None" singleton.
// Note: this macro includes the "return" statement!
Py_RETURN_NONE;
}
注意这里使用了Py_RETURN_NONE
,并且注意它不是return Py_RETURN_NONE
!
PS:这段代码的结构在很大程度上是由个人风格决定的(例如,早期返回、函数内部的static char
格式字符串、初始化为NULL
)。希望重要的信息除了风格约定之外,能清晰易懂。