初学python的C/C++扩展_1

大前提: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++快的因素,就真的
    只能是算法和数据结构的问题了。以前很少关注数据结构和算法的问题,觉得平常用不到,但现在感觉也有必要多了解一下了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值