Python的C/C++扩展
生活随笔
收集整理的這篇文章主要介紹了
Python的C/C++扩展
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Python的C/C++擴(kuò)展
By phidoit@gmail.com
? ? 可擴(kuò)展性是Python的一大特色,一方面,由于Python是解釋執(zhí)行的,這導(dǎo)致運(yùn)行速度會(huì)比編譯型語(yǔ)言慢,因此可以通過(guò)使用C/C++重寫(xiě)核心部分代碼以解決性能上的瓶頸(程序90%的時(shí)間再運(yùn)行10%的代碼);另一方面,可以通過(guò)擴(kuò)展,達(dá)到添加整合一些額外的功能以及保持專(zhuān)有源代碼的目的。在本文接下來(lái)的部分中我們將討論如何編寫(xiě)C/C++擴(kuò)展代碼,并使用它們的功能。
? ? 我們要建立的是一個(gè)可以在Python內(nèi)運(yùn)行的C/C++模塊,因此需要解決如何使C代碼和Python代碼能夠進(jìn)行交互以及數(shù)據(jù)共享。擴(kuò)展是通過(guò)為C代碼編寫(xiě)包裝函數(shù)(類(lèi)似適配器)實(shí)現(xiàn)雙向交互和數(shù)據(jù)共享的。
一.一般的包裝模式
每一個(gè)包裝函數(shù)主要做三件事:
1.把輸入的Python對(duì)象轉(zhuǎn)換為C/C++對(duì)象;
2.調(diào)用C/C++函數(shù);
3.轉(zhuǎn)換C/C++函數(shù)處理的輸出結(jié)果為Python對(duì)象,并返回;
先用一個(gè)簡(jiǎn)單樣例描述模塊大體的過(guò)程:
| //wrap.cpp //1.C代碼 #include "Python.h" int add(int arg1, int arg2) { ? ? return arg1 + arg2; } //2.add的包裝函數(shù): static PyObject* wrap_add(PyObject *self, PyObject *args) { ? ? //把輸入的Python對(duì)象轉(zhuǎn)換為C/C++能識(shí)別的數(shù)據(jù) ? ? int arg1, arg2; ? ? if(!PyArg_ParseTuple(args, "ii", &arg1, &arg2)) ? ? return NULL; ? ? //調(diào)用C/C++函數(shù),得到結(jié)果 ? ? int result = add(arg1,arg2); ? ? //把得到的結(jié)果包裝成Python對(duì)象,并返回 ? ? return (PyObject*)Py_BuildValue("i", result); } //3.為模塊添加PyMethodDef方法數(shù)組 static PyMethodDef wrap_methods[] ={ ? ? {"add", wrap_add, METH_VARARGS}, ? ? {NULL, NULL} }; //4.增加模塊初始化函數(shù)InitModule PyMODINIT_FUNC initwrap (void) { ? ? Py_InitModule("wrap ", wrap_methods); } |
? ?
? ?把上面的代碼編譯,生成wrap.pyd。
? ?Visual Studio 2005編譯方法參考:
? ? http://blog.csdn.net/solo_lxy/archive/2007/07/20/1700515.aspx
? ?啟動(dòng)控制臺(tái)切換到相應(yīng)的工程目錄,即可測(cè)試生成的模塊:
? ?
? ?(PS:似乎只有Release模式生成的模塊才能正常運(yùn)行)
相關(guān)說(shuō)明
? ? 每個(gè)包裝函數(shù)都有如下形式:
? ?? ?? ? PyObject * wrap_function(PyObject *, PyObject * args)
? ? 函數(shù)第一個(gè)參數(shù),有特殊用途,通常選擇忽略。第二個(gè)參數(shù)是一個(gè)PyTuple(PyObject的子類(lèi)型,和Python中的Tuple對(duì)應(yīng)),是調(diào)用時(shí)Python傳入的參數(shù)。
? ? 函數(shù)PyArg_ParseTuple把Python對(duì)象轉(zhuǎn)換為C的數(shù)據(jù)類(lèi)型,其聲明如下:
? ?? ??? int PyArg_ParseTuple(PyObject* args, char* format, ...);
? ? 參數(shù)args必須是一個(gè)tuple對(duì)象,包含傳遞過(guò)來(lái)的參數(shù), format 參數(shù)必須是格式化字符串。剩余參數(shù)是各個(gè)變量的地址,類(lèi)型要與格式化字符串對(duì)應(yīng)。如:
? ?? ??? int arg1, arg2;
? ?? ???PyArg_ParseTuple(args, "ii", &arg1, &arg2);
? ? 函數(shù)Py_BuildValue可以說(shuō)是PyArg_ParseTuple的逆過(guò)程,它把C的數(shù)據(jù)類(lèi)型包裝為Python對(duì)象。
? ?? ??? return (PyObject*)Py_BuildValue("i", result);
? ? 把調(diào)用C函數(shù)的結(jié)果result包裝為Python的int對(duì)象,并返回。
? ?? ???static PyMethodDef wrap_methods[] ={
? ?? ?? ?? ?{"add", wrap_add, METH_VARARGS},
? ?? ?? ?? ?{NULL, NULL}
? ?? ???};
? ? 這個(gè)數(shù)組包含多個(gè)數(shù)組,其中的每個(gè)數(shù)組都包含了一個(gè)函數(shù)的信息,以便解釋器能夠?qū)氩⒄{(diào)用它們,最后一個(gè)NULL數(shù)組表示列表的結(jié)束。 METH_VARARGS常量表示參數(shù)以元組形式傳入。
? ?? ???PyMODINIT_FUNC initwrap (void)
? ?? ???{
? ?? ?? ?? ?Py_InitModule("wrap ", wrap_methods);
? ?? ???}
? ? 模塊初始化函數(shù)void initModuleName(),這部分代碼在模塊被導(dǎo)入的時(shí)候被解釋器調(diào)用。這樣所有的包裝就已經(jīng)完成了。
二.C++類(lèi)的包裝
// Example.cpp class Numbers { public: ? ? Numbers(int first, double second) ? ?? ???: m_first( first), m_second(second){} ? ? double NumMemberMult(void){ return m_first*m_second;} private: ? ? int m_first; ? ? double m_second; }; static void PyDelNumbers(void *ptr) { ? ? Numbers * oldnum = static_cast<Numbers *>(ptr); ? ? delete oldnum; ? ? return; } PyObject *Example_new_Numbers(PyObject *, PyObject* args) { ? ? int arg1; ? ? double arg2; ? ? int ok = PyArg_ParseTuple(args,"id",&arg1,&arg2); ? ? if(!ok) return NULL; ? ?//動(dòng)態(tài)創(chuàng)建一個(gè)新對(duì)象 ? ? Numbers *newnum = new Numbers(arg1, arg2); ? ?//把指針newnum包裝成PyCObject對(duì)象并返回給解釋器 ? ? return PyCObject_FromVoidPtr( newnum, PyDelNumbers); } PyObject * Example_Numbers_MemberMult(PyObject *, PyObject* args) { ? ? PyObject *pynum = 0; ? ? int ok = PyArg_ParseTuple( args, "O", &pynum); ? ? if(!ok) return NULL; ? ?//把PyCObject轉(zhuǎn)換為void指針 ? ? void * temp = PyCObject_AsVoidPtr(pynum); ? ?//把void指針轉(zhuǎn)換為一個(gè)Numbers對(duì)象指針 ? ? Numbers * thisnum = static_cast<Numbers *>(temp); ? ? //調(diào)用函數(shù) ? ? double result = thisnum->NumMemberMult(); ? ? //返回結(jié)果 ? ? return Py_BuildValue("d",result); } static PyMethodDef Example_methods[] = { ? ? {"Numbers", Example_new_Numbers, METH_VARARGS}, ? ? {"NumMemberMult", Example_Numbers_MemberMult, METH_VARARGS}, ? ? {NULL, NULL} }; PyMODINIT_FUNC initExample (void) { ? ? Py_InitModule("Example", Example_methods); } |
? ?C++類(lèi)的包裝和C函數(shù)的包裝大同小異,因?yàn)閷?duì)類(lèi)的包裝是對(duì)函數(shù)的包裝,所以仍需要用Python代碼對(duì)擴(kuò)展模塊進(jìn)行包裝,才可以像類(lèi)一樣的使用。
| #example.py from Example import * class example(object): ? ? def __init__(self,arg1,arg2): ? ?? ???self._base = Numbers(arg1,arg2) ? ? def MemberMult(self): ? ?? ???return NumMemberMult(self._base) |
? ?
? ?這樣C++類(lèi)的包裝也完成了。
三.C/C++中創(chuàng)建Python list
| static PyObject* Windy_dict(PyObject *self, PyObject *args) { ? ? //創(chuàng)建列表 ? ? PyObject *newlist = PyList_New(0); ? ? PyList_Append(newlist, PyString_FromString("first")); ? ? PyList_Append(newlist, PyString_FromString("second")); ? ? PyList_Append(newlist, PyString_FromString("third")); ? ? //返回給解釋器 ? ? return newlist; } |
? ? 創(chuàng)建其它Python對(duì)象也類(lèi)似list的創(chuàng)建,返回給解釋器的都是一個(gè)對(duì)象指針。C/C++對(duì)Python對(duì)象的解析差不多是創(chuàng)建時(shí)的逆過(guò)程。具體的對(duì)象模型及API可以查閱相關(guān)參考文檔。
Python v2.6.2 documentation ? Python/C API Reference Manual ? Concrete Objects Layer
總結(jié)
以上是生活随笔為你收集整理的Python的C/C++扩展的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 用C语言扩展Python的功能的实例
- 下一篇: python-opencv图像处理之用于