先贴一个大牛写的python与C++的通信的经典文章:如何实现 C/C++ 与 Python 的通信?
里面讲到了不少方法来实现C++和python之间的通信,我看了之后深有感触,但里面的例程序大多都是int或者string这样容易转换的,但如果是list呢,应该如何传递到C++中。
于是在stackoverflow上转了之后发现了这么一种方法,
PyObject
"O!" (object)[typeobject, PyObject *]
将Python对象存储在C对象指针中。这类似于“O”,但是接受两个C参数:第一个是Python类型对象的地址,第二个是对象指针存储在其中的C变量(类型为PyObject *)的地址。如果Python对象没有所需的类型,就会引发类型错误(TypeError)。
Python的对象在底层的C语言中就是PyObject,通常用指针去表示也就是PyObject*.
看不懂没关系,直接往下看。
list本身也是个对象,于是"O!"的格式就能将list对应的那个对象的指针赋给pList.
但是我们得到list对象了,如何解析呢,毕竟里面有可能各种类型都有的。
我们必须了解list在C++中的函数库:
此时(假设你用的是linux+python2.7),打开/usr/include/python2.7
你就会看到一堆头文件。
大概就是这样,然后你会发现,里面有个叫listobject.h的东西,这个里头就是对list的一些声明。
#ifndef Py_LISTOBJECT_H
#define Py_LISTOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
PyObject_VAR_HEAD
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
* list.sort() temporarily sets allocated to -1 to detect mutations.
*
* Items must normally not be NULL, except during construction when
* the list is not yet visible outside the function that builds it.
*/
Py_ssize_t allocated;
} PyListObject;
PyAPI_DATA(PyTypeObject) PyList_Type;
#define PyList_Check(op)
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LIST_SUBCLASS)
#define PyList_CheckExact(op) (Py_TYPE(op) == &PyList_Type)
PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);
PyAPI_FUNC(Py_ssize_t) PyList_Size(PyObject *);
PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t);
PyAPI_FUNC(int) PyList_SetItem(PyObject *, Py_ssize_t, PyObject *);
PyAPI_FUNC(int) PyList_Insert(PyObject *, Py_ssize_t, PyObject *);
PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyList_GetSlice(PyObject *, Py_ssize_t, Py_ssize_t);
PyAPI_FUNC(int) PyList_SetSlice(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
PyAPI_FUNC(int) PyList_Sort(PyObject *);
PyAPI_FUNC(int) PyList_Reverse(PyObject *);
PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *);
PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);
风格极度友好,纯C打造,不像某模板库源码天花乱坠。
因为看不到具体的实现,我们只能从声明里猜,还是很好猜的。
我们可以发现:
PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);
这个肯定是创建的函数啦,显而易见。返回值是一个PyObject*.
PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t);
这个就是读取指定项的函数了,英文名也显而易见的。返回值也是PyObject*,毕竟list里面也都是对象,是对象就是PyObject。
PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);
然后这个呢就是添加函数了,第一个参数是list指针,第二个是被你放入的新对象的指针。
然后是PyObject **ob_item,这个是定义在结构体里头的,熟悉C语言的就知道,指针的指针嘛,也就是list的头,所以看到这里你就应该知道,list里其实是装指针的。因为是C写的,所以也没有private属性,你想访问就访问。
其他函数就不解释了,你应该也看得懂。
接下来,如何用这几个函数处理呢?
回到之前那段代码里,有这么一句话:
PyArg_ParseTuple(args, "O!", &PyList_Type, &pList));
这个跑完之后,pList就是指向你的list的指针了。
然后我们要做的就是用PyList_GetItem把list中的每一个东西弄出来(简单起见,在python的代码里就别往list里丢一些乱七八糟的东西了,不然像前面那样得加一堆判断异常什么的,我们现在只放整数)
但这个整数传到C++中可不是整数,是PyObject,
void quick_sort(int *a,int length) {
std::sort(a,a+length);
}
/*
略去大段代码
*/
int *A=new int[n];
for (i=0; i<n; i++)
{
pItem = PyList_GetItem(pList, i);
A[i]=PyInt_AsLong(pItem);
}
quick_sort(A,n) ;
于是,我们得用PyInt_AsLong把每个我读取出来的item做一个转换,从python的整形转为C++的整形,存入我们开辟的空间A中。
然后调用std::sort即可。
但是我们得到了排完序的数组怎么返回给python呢?
PyObject *new_list;
new_list=PyList_New(0);
for(i=0;i<n;i++)
{
PyList_Append(new_list,PyInt_FromLong(A[i]) );
}
很简单,也是用listobject.h中的函数。先用PyList_New来new一个list,然后往里头加数据就可以了,不过你得加PyObject*才行,所以我们用
PyList_Append(new_list,PyInt_FromLong(A[i]) );
来做一个转换。
最后我们把new_list返回去就可以了。具体的一些别的细节如导入导出,编译请看文章首部的链接。
最后奉上完整代码:
这个是C++部分,你得把这编译成一个动态链接库(.so文件)
//文件名:sort.cpp
命令是:g++ -fPIC -shared sort.cpp -o sort.so -I/usr/include/python2.7/ -lpython2.7
然后是python部分
#文件名:aa.py
命令是:python2 aa.py
最后就能看到输出啦:
[0, 1, 2, 3, 4, 5, 6, 7]
整个代码写下来其实没什么用,毕竟你要sort在python里sort一下,也没必要重复造轮子。
但是如果没有人去做这种工作,那某些深度学习框架中调用GPU的操作也就做不出来了。