Python C/C++模块扩展之回调函数传参
在C/C++中,Python的数据类型皆为PyObject*
,所有函数传参和其他的数字、字符串传参没有差异。区别在PyArg_ParseTuple(args, "O", &callback)
的第二个参数 O
(字母O),数字是i
,字符串是s
。
下面会举两个例子,一个是回调函数无参数的,另外一种是回调函数有参数。
Python 回调函数无参数
PyArg_ParseTuple
的第二个参数为O
(字母O),PyObject_CallObject
执行回调函数调用,返回结果。
由于是无参数,所以PyObject_CallObject
第二个参数为NULL
。
1static PyObject* callback(PyObject* self, PyObject* args){
2 PyObject *callback = NULL;
3 PyObject *result = NULL;
4
5 if (!PyArg_ParseTuple(args, "O", &callback)){
6 return nullptr;
7 }
8 result = PyObject_CallObject(callback, NULL);
9 Py_INCREF(result);
10 return result;
11}
Python 回调函数有参数
如果需要传递C/C++中的变量作为参数,传入值回调函数中,我们需要构造一个PyOject *
的结构作为第二个参数传递给PyObject_CallObject
。如下会用到Py_BuildValue
。
Py_BuildValue
和PyArg_ParseTuple
的参数绑定一致,如下:i
表示传递一个int
类型的数值,(i)
表示这是一个int
类型的元祖(tuple),因为函数参数需要一个元祖(tuple)的数据结构。
1static PyObject* callback_with_args(PyObject* self, PyObject* args){
2 PyObject *callback = NULL;
3 PyObject *result = NULL;
4
5 if (!PyArg_ParseTuple(args, "O", &callback)){
6 return nullptr;
7 }
8 int arg = 123;
9 PyObject* arg_list = Py_BuildValue("(i)", arg);
10 result = PyObject_CallObject(callback, arg_list);
11 Py_DECREF(arg_list);
12 if (result == NULL) {
13 return NULL;
14 }
15 Py_INCREF(result);
16 return result;
17}
编程Python程序测试
1import sys
2
3sys.path.append('./callback.so')
4
5import callback
6
7def f():
8 print(2)
9
10def f_with_args(i):
11 print(i)
12
13
14def f_with_args_return(i):
15 return i
16
17
18def main():
19 # 无参数
20 callback.callback(f)
21 # 回调函数有参数
22 callback.callback_with_args(f_with_args)
23 print(callback.callback_with_args(f_with_args_return))
24
25
26if __name__ == "__main__":
27 main()
构建编译及测试
1g++ -fpic -c \
2 -I/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m \
3 callback.cpp
4
5g++ -shared -o callback.so callback.o -lstdc++ \
6 -L/Library/Frameworks/Python.framework/Versions/3.7/lib/ \
7 -lpython3.7m
8
9python test2.py
10
11# output:
12# 2
13# 123
14# 123
扩展
dir
是Python的内置函数,可以帮我们输出变量中的属性以及方法列表。如下是输出callback
模块中的属性及方法列表。
1pirnt(dir(callback))
2# output: ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'callback', 'callback_with_args']