Python读取Postgresql后将数据传递给C++共享库

来源:互联网 发布:vivo手机指纹锁软件 编辑:程序博客网 时间:2024/06/11 21:53


Python调用C++,参数传递的方式有很多种,单个参数传递可以参考一下链接

http://blog.csdn.net/fxjtoday/article/details/6059874

 

下面介绍一下如何使用Python读取Postgresql数据库数据,并传递给调用的C++函数,其实就是扩展一下上面给出的链接的第二种方法。Boost.python库没使用过,不过看上去貌似更加的简单方便,毕竟是再一次的封装,根据一般经验,越往上层封装,编码细节的要求就越低,这样可以把精力放在程序的逻辑架构和业务处理上。所以如果想用好Python + C++编程,学习使用Boost库会使得开发更加得心应手。

直接上代码,分析代码:

下面是C++代码中用到的自定义数据结构:

typedef struct {double x;double y;} GEO_POINT;typedef struct{int link_id;int tilex;int tiley;string road_name;vector<GEO_POINT> vstShplist;} RoadRec;


下面是要编成共享库的C++源码: 

//DoubleMerge.cpp
#include <python2.7/Python.h>//定义Python调用的接口函数PyObject* DoubleMerge(PyObject* self,//这个参数                      PyObject* args)//Python代码传进来的参数,是一个python类型,列表或者字典或者元组等{    PyObject*  datalist = NULL;    PyArg_ParseTuple( args,"O",&datalist );//先从参数列表中解析出来python参数对象    vector<RoadRec> roadlist;//自定义类型    int tilex = 0;    int tiley = 0;    for ( int i = 0; i < PyList_Size(datalist); ++i )    {        //这里python传进来的是数据库表中的数据,是个列表,列表里面的元素都是元组,下面的python代码会看到        //从列表中获取下标为i的元组        PyObject* onerec = PyList_GetItem( datalist,i );        RoadRec road;        //解析元组中的每一个数据        int link_id = PyInt_AsLong( PyTuple_GetItem( onerec,0 ) );        tilex = PyInt_AsLong( PyTuple_GetItem( onerec,1 ) );        tiley = PyInt_AsLong( PyTuple_GetItem( onerec,2 ) );        //cout<<"Link_id:"<<link_id<<"x:"<<tilex<<"y:"<<tiley<<endl;        string road_name = (string)PyString_AsString( PyTuple_GetItem( onerec,3 ) );        string str_geom = (string)PyString_AsString( PyTuple_GetItem( onerec,4 ) );        road.link_id = link_id;        road.road_name = road_name;        //这里ParseGeoPoint函数的作用是将'LINESTRING(0 0,1 1)'这样的字符串中的点解析出来放在一个vector<GEO_POINT>结构中        if ( FAILURE == ParseGeoPoint( str_geom,road.vstShplist ) )        {            return NULL;        }        roadlist.push_back( road );    }    PyObject* resultdata = PyList_New(0);    vector<RoadRec> result;    //上面我们已经将Python传进来的数据表数据转成了C++类型,下面这个函数就是对数据进行处理,    //这里涉及到业务代码,因此不进行扩展    MergeLines(roadlist,result);        //结果出来以后,下面差不多就是上面代码的一个逆过程    int cnt = result.size();    for (int i = 0; i < cnt; ++i)    {        //创建一个元组,其实就是数据表中的一条记录        PyObject* pTuple = PyTuple_New(5);        RoadRec& link = result.at(i);        string str_geo;        //将vector<GEO_POINT>这样的结构打包成'LINESTRING(0 0,1 1)'这样的字符串        PacketGeoPoint( link.vstShplist,str_geo );        //设置元组(一条数据表记录)中的所有字段的值        PyTuple_SetItem( pTuple,0,Py_BuildValue( "i",link.link_id ) );        PyTuple_SetItem( pTuple,1,Py_BuildValue( "i",tilex ) );        PyTuple_SetItem( pTuple,2,Py_BuildValue( "i",tiley ) );        PyTuple_SetItem( pTuple,3,Py_BuildValue( "s",link.road_name.c_str() ) );//不能传入string类型,应该传入char*类型        PyTuple_SetItem( pTuple,4,Py_BuildValue( "s",str_geo.c_str() ) );        //所有的记录都添加到列表中,这个列表就代表数据库一张表的数据        PyList_Append( resultdata,pTuple );    }    //返回列表,其实就是返回的一张数据表    return resultdata;}//这个函数现在还没有弄太清楚,就是知道函数的名字就是上面那个函数DoubleMerge前面加上init//如果你的Python代码表调用的函数是DoubleMerge,这个函数名就必须定义为initDoubleMerge,//应该是在python导入C++库的时候"通知"DoubleMerge是可以调用的函数吧,原理不太清楚//METH_VARARGS是参数传递的标准形式,它通过Python的元组在Python解释器和C函数之间传递参数,所以上面有这么一个操作//PyArg_ParseTuple( args,"O",&datalist );//若采用METH_KEYWORD方式,则Python解释器和C函数之间将通过Python的字典类型在两者之间进行参数传递//关于下面这个函数的详细说明,可以参考一下链接//http://www.ibm.com/developerworks/cn/linux/l-pythc/PyMODINIT_FUNC initDoubleMerge(void){    static PyMethodDef methods[] = {    {"DoubleMerge", (PyCFunction)DoubleMerge, METH_VARARGS, "merge double lines"},    {NULL, NULL, 0, NULL}    };    Py_InitModule("DoubleMerge", methods);}

将上述代码编译成共享库:g++ -o -DoubleMerge.so DoubleMerge.cpp -fpic -shared


下面贴上Python的代码

import psycopg2import psycopg2.extrasimport DoubleMerge      #导入编译好的C++库#设置一下链接数据库的相关信息DATABASE_HOST = "127.0.0.1"DATABASE_PORT = 5432DATABASE_NAME = "test"DATABASE_USERNAME = "postgres"DATABASE_PASSWORD = "postgres"if __name__ == "__main__":    #链接数据库    conn = psycopg2.connect(database=DATABASE_NAME, user="postgres", password="postgres", host=DATABASE_HOST, port="5432")    cur = conn.cursor()    sql = "select gid,tilex,tiley,road_name,st_astext(st_transform(the_geom,900913))\            from roadlist where road_name is not null"     #执行SQL语句    cur.execute(sql)    #获取所有记录    rows = cur.fetchall()    #如果print一下rows,我们会发现,打印的结果是:    #[(字段1,字段2,字段3),(字段1,字段2,字段3),(字段1,字段2,字段3),(字段1,字段2,字段3)]    #可以看出,数据库表中的一条记录,读到python中就是一个元组,所有字段又放在一个列表中    #因为本实例读取的是空间道路数据,空间数据的组织形式是按tile组织,    #因此我们根据tilex和tiley,算出tileid,并把列表数据按照不同的tileid组织成字典    sourcedata = {}    for i in rows:        tileid = i[1] << 8 | i[2]        if tileid in sourcedata:            sourcedata[tileid].append(i)        else:            sourcedata[tileid] = [i]    #print sourcedata        print "Create newtable table..."    #创建我们要写入的新表    sql = "drop table if exists newtable;\                    create table newtable(link_id integer,tilex integer,tiley integer ,road_name character varying);\                    select addgeometrycolumn('newtable','the_geom',900913,'LINESTRING',2);commit;"    cur.execute( sql )    #result存放的是一个tile的所有记录    result = []    #循环处理所有tile    for tileid in sourcedata:        print "Tile ID:" ,tileid        result = DoubleMerge.DoubleMerge( sourcedata[tileid] ) #此处调用的就是我们编译好的C++库函数        #将结果一条一条插入数据库中        for re in result:            link_id,tilex,tiley,roadname,strgeo = re            values = str(link_id) + "," + str(tile_8_x) + "," + str(tile_8_y) + ","            values = values + "$$" + roadname + "$$,st_geomfromtext('LINESTRING(" + strgeo + ")',900913)"            sql = "insert into newtable(link_id,tilex,tiley,road_name,the_geom) values("+values+");"            cur.execute( sql )    conn.close()
这样就完成了在Python中读取Postgresql数据库中的数据,并将数据传递给C++代码,处理以后再写入到Postgresql数据库中,都说python是个很好的语言,因为刚接触不久,不敢妄作评论,不过python开发确实可以忽略很多C/C++开发的细节,还可以很方便的嵌入C/C++,java等常用语言,这样就可以取各种语言之所长,不仅开发效率高,代码执行效率也不低,不过这样开发带来一个问题,就是调试,不能像VS,VC,Eclipse单调一种语言这么的方便,或许有很好的调试技巧,只是我还没有用到。



0 0