从C、C++语言编写的程序中调用Python可以加快编程速度,充分利用Python编程的便捷性。
需要理解的问题:
支持callback函数的库
Callback在维基上的解释是:在计算机编程中,一个callback是一段可执行代码,它作为参数传递给其他代码,以在适当的时候使这段参数代码被调用执行(call back/execute)。它有同步callback和异步callback二种,取决于其他代码与callback代码的调用时间之间的关系。
在C语言中调用Python时,C语言代码利用callbacks来完成对Python的调用。那么,在C语言的范畴中,Python的等价描述(equivalent)中就需要为Python编程人员提供一种callback的机制;实现中,就需要在C语言的callback代码中调用Python的callback代码。Python的两个特点为上述功能的实现提供了方便:首先,Python的解释器可以被递归得调用;其次,存在调用Python函数的标准接口。
详细的调用是这样的,首先Python程序必须以某种方式提供Python函数对象,这里我们可以用一个函数或者接口来完成这个功能。当这个函数或者接口被调用的时候,在全局变量(或其他合适的域)中保存这个函数(或接口)对象的指针。下面是一个例子:
static PyObject *my_callback = NULL;
static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
PyObject *result = NULL;
PyObject *temp;
if(PyArg_ParseTuple(args, "O:set_callback", &temp))
{
if(!PyCallable_Check(temp))
{
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp); //为新生成callback代码对象增加一个引用
Py_XDECREF(my_callback); //处理my_callback之前引用的对象
my_callback = temp; //用my_callback保存当前的callback代码引用
// 用于返回None的样板
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}
用于提供函数对象的函数必须在解释器里注册,详细过程如下:
static PyMethodDef SetCallbackMethods[] = {
...
{
"SetCallback", //方法的名称
my_set_callback, //指向C实现的指针
METH_VARARGS, //用于指示如何构造调用的比特标志
"set the callback code piece object" //指向相关文档内容的指针
},
...
{NULL, NULL, 0, NULL} //标记
};
上面代码中的PyMethodDef是一个结构体(方法表),用于描述Python中扩展类型的方法。主要有4个域
域 | C类型 | 含义 |
ml_name | char * | 方法的名称 |
ml_method | PyCFunction | 指向C实现的指针 |
ml_flags | int | 用于指示如何构造调用的比特标志 |
ml_doc | char * | 指向相关文档内容的指针 |
其中,ml_method是一个C函数指针,它所指向的函数全部返回PyObject * 类型的引用(新引用,在Python中可见)。PyCFunction是C语言中用于实现可调用Python模块的典型函数类型。这类函数有2个PyObject*类型的参数(需要理解Python中self的C类型表示)。ml_flags包含calling或者binding两种协议,如果是0则表示使用PyArg_ParseTuple()变量(已过时)。PyArg_ParseTuple(PyObject *args, const char *format, ...)用于解析一个函数的参数,并按照位置把参数保存到本地变量中。如果ml_flags是METH_VARARGS,则函数只应该接收Python级(由PyArg_ParseTuple()函数来解析)的参数;如果参数接收关键字字典,则METH_KEYWORDS比特位应该置位(METH_VARARGS|METH_KEYWORDS),对应的函数应该有第三个PyOjbect*类型的参数用于接收关键字字典(由PyArg_ParseTupleAndKeywords()来解析)。
在Python模块初始化阶段,符号表必须要给到解释器。初始化函数要命名为initname(),其中name是模块的名称,这个函数是模块文件中唯一的非静态成员。例如,
PyMODINIT_FUNC
initSetCallback(void)
{
(extern "C") Py_InitModule("SetCallback", SetCallbackMethods);
}
当Python程序第一次imports “SetCallback”模块时,initSetCallback被调用。Py_InitModule()生成一个新的module对象,并在sys.modules 字典中插入以SetCallback为key的元素,根据Py_InitModule的第二个参数(PyMethodDef型数组),将内建的函数对象插入到新模块中,如果成功,返回指向这个新模块的指针。
当在C或C++代码中嵌入Python代码时,initSetCallback如果在_PyImport_Inittab表中有记录,就会被自动调用;否则,需要在代码中直接调用,即initSetCallback(),例如:
int main(int argc, char *argv[])
{
// 向解释器传递第一个参数
Py_SetProgramName(argv[0]);
// 初始化一个Python解释器,这个是必需的
Py_Initialize();
// 增加一个静态的模块
initSetCallback();
......
}
关于PyArg_ParseTuple(PyObject *arg, char *format, ...),arg 是一个包含参数列表的tuple对象,由Python传递到C函数里,format字符串由0到多个格式单元组成,一个格式单元描述一个Python对象,多个格式单元用括号包围,逗号分隔。