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單調一種語言這麼的方便,或許有很好的調試技巧,只是我還沒有用到。