Python讀取Postgresql後將數據傳遞給C++共享庫



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



發佈了33 篇原創文章 · 獲贊 11 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章