您的位置:首页 > 编程语言 > C语言/C++

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

2015-07-22 10:21 731 查看


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 psycopg2
import psycopg2.extras
import DoubleMerge      #导入编译好的C++库

#设置一下链接数据库的相关信息
DATABASE_HOST = "127.0.0.1"
DATABASE_PORT = 5432
DATABASE_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单调一种语言这么的方便,或许有很好的调试技巧,只是我还没有用到。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: