上一节介绍了编写扩展的基本流程。这一回介绍一下在扩展模块中的函数调用,包括在扩展函数的参数提取和关键字参数解析,以及在C语言中调用Python方法。
同样的本文中的示例代码可从 https://github.com/wusuopu/python-c-extension-sample 获取到。
参数提取
接着上一节的例子,我们继续编辑lc_hello.c文件。先往模块中添加一个名为 func1 的函数,即就是在 lc_hello_world_methods 数组中添加一项:
{"func1", (PyCFunction)func1_function, METH_VARARGS, NULL},
然后就是对该函数的实现。参数提取是使用 PyArg_ParseTuple 方法,其定义如下:
int PyArg_ParseTuple(PyObject *arg, char *format, ...);
其中 arg 参数为Python向C函数传递的参数列表,是一个无组对象;format 参数是一个格式化字符串,它的格式可以参考 Python/C API 文档。func1_function 函数实现如下:
static PyObject* func1_function(PyObject *self, PyObject *args){ int num, i, j; long lnum=0; const char* s1 = NULL; PyObject *obj = NULL; if (!PyArg_ParseTuple(args, "is(ii)|l", &num, &s1, &i, &j, &lnum)) { printf("传入参数错误!\n"); return NULL; } printf("num: %d\tstr1: %s\n" "i: %d\tj: %d\tlnum: %ld\n", num, s1, i, j, lnum); obj = Py_BuildValue("{sisisislss}", "num", num, "i", i, "j", j, "lnum", lnum, "s1", s1); return obj;}
在Python中该函数可以接收3个或者4个参数。同时该函数使用了 Py_BuildValue 方法构造了一个字典对象并返回。Py_BuildValue的用法与PyArg_ParseTuple类似。接下来可以在Python中进行测试:
print(lc_hello_world.func1(11, 'abc', (2, 3), 100))print(lc_hello_world.func1(11, 'abc', (2, 3)))
关键字参数
再在 lc_hello_world_methods 数组中添加一项:
{"func2", (PyCFunction)func2_function, METH_VARARGS | METH_KEYWORDS, NULL},
关键字参数解析是使用 PyArg_ParseTupleAndKeywords 方法,其定义如下:
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, char *format, char *kwlist[], ...);
其中 arg 参数和 format 参数与PyArg_ParseTuple一样。kwdict参数是一个字典对象,保存了关键字参数。kwlist是一个以NULL结尾的字符串数组。func2_function 函数实现如下:
static PyObject* func2_function(PyObject *self, PyObject *args, PyObject *kwargs){ int voltage; char *state = "a stiff"; char *action = "voom"; char *type = "Norwegian Blue"; static char *kwlist[] = {"voltage", "state", "action", "type", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|sss", kwlist, &voltage, &state, &action, &type)) return NULL; printf("-- This parrot wouldn't %s if you put %i Volts through it.\n", action, voltage); printf("-- Lovely plumage, the %s -- It's %s!\n", type, state); Py_INCREF(Py_None); return Py_None;}
接下来可以在Python中进行测试:
lc_hello_world.func2(state="ok", action="test", type="func", voltage=13)lc_hello_world.func2(20)
在扩展模块中调用Python方法
在扩展模块中可以使用 PyObject_CallObject 方法来调用Python的函数方法。其定义如下:
PyObject* PyObject_CallObject(PyObject *callable_object, PyObject *args)
再在 lc_hello_world_methods 数组中添加一项:
{"func3", (PyCFunction)func3_function, METH_VARARGS, NULL},
func3_function 函数实现如下:
static PyObject* func3_function(PyObject *self, PyObject *args){ PyObject *my_callback = NULL; PyObject *result = NULL; PyObject *arg = NULL; if (!PyArg_ParseTuple(args, "OO:set_callback;argument;", &my_callback, &arg)) { printf("传入参数错误!\n"); return NULL; } if (!PyCallable_Check(my_callback)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } result = PyObject_CallObject(my_callback, arg); if (!result) { Py_INCREF(Py_None); result = Py_None; } return result;}
接下来可以在Python中进行测试:
print(lc_hello_world.func3(int, (1.234, )))