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