大前提:python的C/C++扩展有不少现成的东西,你可能完全没必要看这里的东西。这里的东西的存在的意义可能就是为了“玩”,说得好听点就是探索/学习。
本来是没有这一篇的,这部分的东西的学习只是作为原来目的的额外扩展的,但是原问题的除了在很大程度上打击自己,让自己最近浮躁的心安定下来外,没别的什么好说的,所以只好把这里的额外的东西拿来说说。
在上一篇“初学python的C/C++扩展”,不才在下勉强说了点python的C/C++扩展的东西,但那是远远不够的。前天手贱,从python.org上下载了Python 2.7的文档(http://docs.python.org/2/download.html),里面有c-api.pdf,然后再次手贱的点了进去,发现里面东西真不少。于是在感叹又有不少可以学的东西的时候,又汗颜自己之前写的就是渣啊。。
OK,闲话少说,既然叫做“初学”,那么菜是难免的。王安石在《游褒禅山记》中写道“尽吾志而不能至者可以无悔矣”,虽然在下才疏智浅,连巨人在哪里都看不到,但仍愿意贯彻自己,仅以薄力,帮助那些将来可以站在巨人肩膀上的人在初期的学习过程中,在基础的问题上有所斩获。
好吧,还是说了些废话。
上一篇“初学”,还有些地方需要强调一下:
#0, 在*.cpp 中的
PyMODINIT_FUNC initModuleName(void){
(void)Py_InitModule("ModuleName", methods);
}
这里的两个ModuleName需要一致,并且和最后编译成的动态库的名字一致 ModuleName.so。否则,当你在python下 import ModuleName 时会报错:
ImportError:dynamic module does not define init function ( initModuleName)
#1,需要编译出动态库,才能在python下import,所以你至少应该有这样的编译命令:
gcc/g++ -fPIC -shared -o ModuleName.so *.cpp
# python-2.7
import random
list = range(1500)
random.shuffle(list)
这样list就是我们要输入的序列了。接下来就出问题了,将这个list传给我的python程序很简单,但是如何传给一个C/C++ 程序呢?一开始我想到的是:将这个序列写入一个文件,然后让C/C++程序去读那个文件。但感觉步骤有点多,于是放弃了。接下来的思路就是为什么不把作为对比的C/C++程序写成一个模块呢?
#1,怎么办?
>>>一开始的时候,虽然手里有下载了的Python2.7的文档,但觉得研读起来太费时间,所以还是选择了上网查。这次,stackoverflow 上的东西没能及时拯救我的问题。 反而是这里——http://mail.python.org/pipermail/tutor/2005-February/035686.html,给出了有力的例子。可以直接看连接,作者的英文用词/语很易懂(其实也扯了一些有的没的)。在这里,我结合我的例子给出一点点说明。
#2,例子/代码。
A,Python list/tuple --> C/C++ array
除去上一篇“初学”中的一些东西之外,在*.cpp文件里,你至少应该有:
static PyObject* swapFunc(PyObject* self, PyObject *args)
{
//假设我们要转过来的是一个整型数的python序列
PyObject *i_arr, *item;
int length; // 转过来的python序列的长度
if(!PyArg_ParseTuple(args, "iO", &length, &i_arr)){ // i for int, O for PyObject
return NULL;
}
if(!PySequence_Check(i_arr)){
PyErr_SetString(PyExc_TypeError, "expected sequence");
return NULL;
}
int a[length+1]; // 此数组用来保存转过来的python序列
for(int i=0;i!=length;i++){
item = PySequence_GetItem(i_arr,i);
if(!PyInt_Check(item)){
PyErr_SetString(PyExc_TypeError, "expected sequence of integers");
return NULL;
}
a[i] = PyInt_AsLong(item);
Py_DECREF(item);
}
// swap finished..
other_process_func();
...
}
之后,在python下,你就可以这样用了:
import theModule
list = [...]
theModule.swapFunc( len(list), list )
除了Py_DECREF(item) 以外,几乎没什么需要特别说明的,如果对于上面一些函数/方法的用法问题,你应该去下载一个Python的文档,并且自己动手写一写。值得注意的是,你不一定需要传入len(list), 其他版本的情况不清楚,但是Python 2.7下,你可以这样用:
int size = PySequence_Size(i_arr);
来取得传入的python list的长度。另外,对于PyInt_AsLong(item),这里需要考虑一下Python支持的数字类型的情况,所以有长整型,而非整型。
B,C/C++ array --> Python list/tuple:
在*.cpp文件里,你至少应该有:
Python* swapToPy(int i_arr[], int length)
{
PyObject *item, *pyList;
pyList = PyList_New(length);
for(int i=0;i!=length;i++){
item = PyInt_FromLong(i_arr[i]);
PyList_SetItem(pyList, i, item);
Py_DECREF(item);
}
return pyList;
}
之后,在python下,你就可以这样用了:
import theModule
list = theModule.swapToPy(...)
list将会是一个python list.
上面的内容中,不才在下跳过了Py_DECREF,因为这个地方,我也不是特别的明白,所以就不乱说了。Py_DECREF 和 Py_INCREF 是用来减少和添加一个对象的引用数,而引用数关系到Python内存管理的,你不能拥有一个对象,但是可以拥有一个对象的引用,一个对象的引用数变为0之后,在之后的某时间,Python会对其进行回收。其他的就真的不敢乱说了。
#3,接下来?
>>>上面只给出了List <--> array 的转换例子,但我想C++下 vector,list ,Python的tuple,这些的也应该不用多说了。接下来,如果还有探索的小兴趣,那么可以试试转换 Python dict。如果不介意对于Py_DECREF的跳过,那么可以看看下面的对于转换Python dict的例子。当然和上面的例子一样,都是最简单最基本的例子,也就是没有展示 更多功能了。
A,Python dict --> C char *key[], int *value[].
可能看到这里,你就已经发现了问题。Python的字典可以存储很丰富的内容,但是转化出来的结果可能就不具有那样效果了。当然我这里只是给一个简单的例子,如果能抛 砖引玉就够了。其实能够转化的字典有很多限制,比键值对能翻转的字典还要有限制。
char *key[100]; // 假设我们已经知道要处理的字典就这么大
char value[100];
PyObject *py_dic, *py_item, *py_keys, *py_values;
if(!PyArg_ParseTuple(args, "O", &py_dic)){
return NULL;
}
if(!PyDict_Check(py_dic)){
PyErr_SetString(PyExc_TypeError, "expected dictionary");
return NULL;
}
int size = PyDict_Size(py_dic);
py_keys = PyDict_Keys(py_dic);
py_values = PyDict_Values(py_dic);
for(int i=0;i!=size;i++){
py_item = PyList_GetItem(py_keys, i);
if(!PyString_Check(py_item)){
PyErr_SetString(PyExc_TypeError, "dictionary key error!");
return NULL;
}
key[i] = PyString_AsString(py_item);
Py_DECREF(py_item);
py_item = PyList_GetItem(py_values, i);
if(!PyInt_Check(py_item)){
PyErr_SetString(PyExc_TypeError, "dictionary value error!");
return NULL;
}
value[i] = PyInt_AsLong(py_item);
Py_DECREF(py_item);
}
// swap finished.
other_process_func();
}
B, C char *key[], int *value[] --> Python dict
PyObject * retPyDic(char *key[], int value[], int length)
{
PyObject *py_value, *dic;
dic = PyDic_New();
if(dic){
for(int i=0;i!=length;i++){
py_value = PyInt_FromLong(value[i]);
PyDict_SetItemString(dic, key[i], py_value);
Py_DECREF(py_value);
}
}else{
cout<<"error in new_dict..."<<endl;
}
return dic;
}
以上,同样我忽略了对Py_DECREF的探究,也同样的,更多细节,只要你有一个Python的文档,大都基本不是问题。
#4,探究结束。
以上,就是对Python的学习内容了。
题外话:
我用Python写的不用动态规划的方法解决最长非降序子序列的问题的代码,放在了个人空间的代码分页里。实践后发现,明显的比网上普遍找到
动态规划的C/C++实现慢,序列长度N,N=7K~8K的时候还可以做到秒出,N=15K的时候,统计了几次基本都在6s~7s。除去Python没有C/C++快的因素,就真的
只能是算法和数据结构的问题了。以前很少关注数据结构和算法的问题,觉得平常用不到,但现在感觉也有必要多了解一下了。
本来是没有这一篇的,这部分的东西的学习只是作为原来目的的额外扩展的,但是原问题的除了在很大程度上打击自己,让自己最近浮躁的心安定下来外,没别的什么好说的,所以只好把这里的额外的东西拿来说说。
在上一篇“初学python的C/C++扩展”,不才在下勉强说了点python的C/C++扩展的东西,但那是远远不够的。前天手贱,从python.org上下载了Python 2.7的文档(http://docs.python.org/2/download.html),里面有c-api.pdf,然后再次手贱的点了进去,发现里面东西真不少。于是在感叹又有不少可以学的东西的时候,又汗颜自己之前写的就是渣啊。。
OK,闲话少说,既然叫做“初学”,那么菜是难免的。王安石在《游褒禅山记》中写道“尽吾志而不能至者可以无悔矣”,虽然在下才疏智浅,连巨人在哪里都看不到,但仍愿意贯彻自己,仅以薄力,帮助那些将来可以站在巨人肩膀上的人在初期的学习过程中,在基础的问题上有所斩获。
好吧,还是说了些废话。
上一篇“初学”,还有些地方需要强调一下:
#0, 在*.cpp 中的
PyMODINIT_FUNC initModuleName(void){
(void)Py_InitModule("ModuleName", methods);
}
这里的两个ModuleName需要一致,并且和最后编译成的动态库的名字一致 ModuleName.so。否则,当你在python下 import ModuleName 时会报错:
ImportError:dynamic module does not define init function ( initModuleName)
#1,需要编译出动态库,才能在python下import,所以你至少应该有这样的编译命令:
gcc/g++ -fPIC -shared -o ModuleName.so *.cpp
现在开始进入本次的初学阶段——C/C++ 数组 <--> Python list/tuple的转换。
#0,我为什么会遇到这个问题?
>>> 在求一个(数值)序列的最长非降序子序列的问题时,因为在下是半路出家的小白,连动态规划都没听过,所以只好自己构思着,在python下写了个程序来解决。在和同学 交流的时候,我们都认为处理结果的正确性没有问题,主要使用python的序列是可动态增减的,所以空间上没有考虑,但时间上的消耗就不好说了。虽然知道自己的程序在 数据量稍微有点大的时候就肯定会跪,但还是很好奇,到底与别人用C/C++写的动态规划的差距有多大,所以决定设计点东西,将二者比较一下。对于输入的随机序列,用 python做起来很方便:# python-2.7
import random
list = range(1500)
random.shuffle(list)
这样list就是我们要输入的序列了。接下来就出问题了,将这个list传给我的python程序很简单,但是如何传给一个C/C++ 程序呢?一开始我想到的是:将这个序列写入一个文件,然后让C/C++程序去读那个文件。但感觉步骤有点多,于是放弃了。接下来的思路就是为什么不把作为对比的C/C++程序写成一个模块呢?
#1,怎么办?
>>>一开始的时候,虽然手里有下载了的Python2.7的文档,但觉得研读起来太费时间,所以还是选择了上网查。这次,stackoverflow 上的东西没能及时拯救我的问题。 反而是这里——http://mail.python.org/pipermail/tutor/2005-February/035686.html,给出了有力的例子。可以直接看连接,作者的英文用词/语很易懂(其实也扯了一些有的没的)。在这里,我结合我的例子给出一点点说明。
#2,例子/代码。
A,Python list/tuple --> C/C++ array
除去上一篇“初学”中的一些东西之外,在*.cpp文件里,你至少应该有:
static PyObject* swapFunc(PyObject* self, PyObject *args)
{
//假设我们要转过来的是一个整型数的python序列
PyObject *i_arr, *item;
int length; // 转过来的python序列的长度
if(!PyArg_ParseTuple(args, "iO", &length, &i_arr)){ // i for int, O for PyObject
return NULL;
}
if(!PySequence_Check(i_arr)){
PyErr_SetString(PyExc_TypeError, "expected sequence");
return NULL;
}
int a[length+1]; // 此数组用来保存转过来的python序列
for(int i=0;i!=length;i++){
item = PySequence_GetItem(i_arr,i);
if(!PyInt_Check(item)){
PyErr_SetString(PyExc_TypeError, "expected sequence of integers");
return NULL;
}
a[i] = PyInt_AsLong(item);
Py_DECREF(item);
}
// swap finished..
other_process_func();
...
}
之后,在python下,你就可以这样用了:
import theModule
list = [...]
theModule.swapFunc( len(list), list )
除了Py_DECREF(item) 以外,几乎没什么需要特别说明的,如果对于上面一些函数/方法的用法问题,你应该去下载一个Python的文档,并且自己动手写一写。值得注意的是,你不一定需要传入len(list), 其他版本的情况不清楚,但是Python 2.7下,你可以这样用:
int size = PySequence_Size(i_arr);
来取得传入的python list的长度。另外,对于PyInt_AsLong(item),这里需要考虑一下Python支持的数字类型的情况,所以有长整型,而非整型。
B,C/C++ array --> Python list/tuple:
在*.cpp文件里,你至少应该有:
Python* swapToPy(int i_arr[], int length)
{
PyObject *item, *pyList;
pyList = PyList_New(length);
for(int i=0;i!=length;i++){
item = PyInt_FromLong(i_arr[i]);
PyList_SetItem(pyList, i, item);
Py_DECREF(item);
}
return pyList;
}
之后,在python下,你就可以这样用了:
import theModule
list = theModule.swapToPy(...)
list将会是一个python list.
上面的内容中,不才在下跳过了Py_DECREF,因为这个地方,我也不是特别的明白,所以就不乱说了。Py_DECREF 和 Py_INCREF 是用来减少和添加一个对象的引用数,而引用数关系到Python内存管理的,你不能拥有一个对象,但是可以拥有一个对象的引用,一个对象的引用数变为0之后,在之后的某时间,Python会对其进行回收。其他的就真的不敢乱说了。
#3,接下来?
>>>上面只给出了List <--> array 的转换例子,但我想C++下 vector,list ,Python的tuple,这些的也应该不用多说了。接下来,如果还有探索的小兴趣,那么可以试试转换 Python dict。如果不介意对于Py_DECREF的跳过,那么可以看看下面的对于转换Python dict的例子。当然和上面的例子一样,都是最简单最基本的例子,也就是没有展示 更多功能了。
A,Python dict --> C char *key[], int *value[].
可能看到这里,你就已经发现了问题。Python的字典可以存储很丰富的内容,但是转化出来的结果可能就不具有那样效果了。当然我这里只是给一个简单的例子,如果能抛 砖引玉就够了。其实能够转化的字典有很多限制,比键值对能翻转的字典还要有限制。
static PyObject *swapFunc2(PyObject *self, PyObject *args)
{char *key[100]; // 假设我们已经知道要处理的字典就这么大
char value[100];
PyObject *py_dic, *py_item, *py_keys, *py_values;
if(!PyArg_ParseTuple(args, "O", &py_dic)){
return NULL;
}
if(!PyDict_Check(py_dic)){
PyErr_SetString(PyExc_TypeError, "expected dictionary");
return NULL;
}
int size = PyDict_Size(py_dic);
py_keys = PyDict_Keys(py_dic);
py_values = PyDict_Values(py_dic);
for(int i=0;i!=size;i++){
py_item = PyList_GetItem(py_keys, i);
if(!PyString_Check(py_item)){
PyErr_SetString(PyExc_TypeError, "dictionary key error!");
return NULL;
}
key[i] = PyString_AsString(py_item);
Py_DECREF(py_item);
py_item = PyList_GetItem(py_values, i);
if(!PyInt_Check(py_item)){
PyErr_SetString(PyExc_TypeError, "dictionary value error!");
return NULL;
}
value[i] = PyInt_AsLong(py_item);
Py_DECREF(py_item);
}
// swap finished.
other_process_func();
}
B, C char *key[], int *value[] --> Python dict
PyObject * retPyDic(char *key[], int value[], int length)
{
PyObject *py_value, *dic;
dic = PyDic_New();
if(dic){
for(int i=0;i!=length;i++){
py_value = PyInt_FromLong(value[i]);
PyDict_SetItemString(dic, key[i], py_value);
Py_DECREF(py_value);
}
}else{
cout<<"error in new_dict..."<<endl;
}
return dic;
}
以上,同样我忽略了对Py_DECREF的探究,也同样的,更多细节,只要你有一个Python的文档,大都基本不是问题。
#4,探究结束。
以上,就是对Python的学习内容了。
题外话:
我用Python写的不用动态规划的方法解决最长非降序子序列的问题的代码,放在了个人空间的代码分页里。实践后发现,明显的比网上普遍找到
动态规划的C/C++实现慢,序列长度N,N=7K~8K的时候还可以做到秒出,N=15K的时候,统计了几次基本都在6s~7s。除去Python没有C/C++快的因素,就真的
只能是算法和数据结构的问题了。以前很少关注数据结构和算法的问题,觉得平常用不到,但现在感觉也有必要多了解一下了。