Python调用C++,参数传递的方式有很多种,单个参数传递可以参考一下链接
http://blog.youkuaiyun.com/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单调一种语言这么的方便,或许有很好的调试技巧,只是我还没有用到。