vlambda博客
学习文章列表

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_BuildValuePyArg_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']