C++调用Python函数
From: http://www.flatws.cn/article/program/c/2010-08-24/9677.html
?? ? Python代碼在實(shí)現(xiàn)某些功能的時(shí)候非常方便,如果能夠?qū)ython代碼與C++程序結(jié)合起來(lái),那么一定會(huì)使Problem Solving方便許多(比如,游戲腳本系統(tǒng))~
?? ?從學(xué)完P(guān)ython開(kāi)始就一直想研究一下C++內(nèi)嵌Python是怎么回事,可是每次都堅(jiān)持不下來(lái)。。。今天上校內(nèi),偶然間看到同學(xué)求助,怎么實(shí)現(xiàn)輸入表達(dá)式輸出結(jié)果~我想到了Python的builtin函數(shù)eval,正好借這個(gè)機(jī)會(huì)研究一下怎么實(shí)現(xiàn)C++調(diào)用Python函數(shù)!
?? ?假定您的系統(tǒng)中已經(jīng)安裝了Python,我使用的版本是2.6~
?? ?1.配置IDE。
?? ?我用的是VS2008,只需要將Python安裝路徑下的include文件夾和libs文件夾添加到C++目錄中即可。步驟是:Tools -> Options -> Project & Solution -> VC++ Dir選擇右上的Dropdown List,分別將include文件夾和libs文件夾添加到include和lib項(xiàng)中。
?? ?2.編寫(xiě)Python腳本。
?? ?為了完成自動(dòng)計(jì)算值的功能,我寫(xiě)了如下的腳本(文件名是python_code.py):
def calculate(expression):try: result = eval(expression) except:print 'Eval error!'return Nonereturn result??? 將這個(gè)文件與C++程序文件放到同一目錄下,這樣保證編譯器可以根據(jù)相對(duì)路徑找到。
?? ?3.Python嵌入的方式 - 高級(jí)vs低級(jí)
?? ?接下來(lái)進(jìn)入C++內(nèi)嵌Python函數(shù)的關(guān)鍵部分了。根據(jù)官方的幫助文檔,有兩個(gè)類(lèi)型的Python調(diào)用,High Level與Low Level。High Level就是調(diào)用者不需要管理與Python相關(guān)的內(nèi)存,只有3行代碼即可,分別是Py_Initialize()來(lái)初始化解釋器(注意,此時(shí)程序仍然是由C++的編譯器進(jìn)行編譯),調(diào)用PyRun_SimpleString()其中參數(shù)是要執(zhí)行的Python代碼,如果是一個(gè)確定的Python文件,那么我們可以調(diào)用PyRun_SimpleFile()直接執(zhí)行Python文件。最后調(diào)用Py_Finalize()方法結(jié)束解釋器工作。
?? ?使用High Level對(duì)程序員的要求非常少,基本就是會(huì)寫(xiě)Python代碼就會(huì)在C++中調(diào)用Python的方法,但是弊端就是當(dāng)前程序不能與Python的方法進(jìn)行數(shù)據(jù)的交互,所以局限性非常大。
?? ?所以,如果我們需要與Python程序進(jìn)行交互,比如計(jì)算表達(dá)式的值,我們只能使用低級(jí)的方法了。
?? ?4.Low Level程序編寫(xiě)步驟。
?? ?說(shuō)實(shí)話,以前幾次當(dāng)我想學(xué)習(xí)Python內(nèi)嵌的時(shí)候都是因?yàn)榭吹綖榱诉M(jìn)行Low Level調(diào)用而編寫(xiě)的冗長(zhǎng)的C++代碼,實(shí)在是太恐怖了。。可是今天靜下來(lái)看一看其實(shí)只有4步:
?? ?(1)導(dǎo)入要用到的模塊:
?? ? 要導(dǎo)入模塊其實(shí)很簡(jiǎn)單,首先我們需要提供模塊的文件名,注意,跟Python中導(dǎo)入模塊一樣,這里的文件名不能包含".py"后綴,比如我的文件名是python_code.py,我在程序中只需要提供"python_code"這個(gè)字符串就可以了。這一部分代碼如下(注意使用到的兩個(gè)函數(shù):PyString_FromString和PyImport_Import):
PyObject *pyFileName = PyString_FromString(filename.c_str()); PyObject *pyMod = PyImport_Import(pyFileName); // load the module??? 注意,在C++中一切Python的object都是用PyObject*來(lái)代替。第一行語(yǔ)句將C++中的char數(shù)組轉(zhuǎn)化為Python中的字符串,然后利用這個(gè)文件名導(dǎo)入模塊!請(qǐng)注意函數(shù)是PyImport_Import,而不是別的。
?? ?好了,模塊導(dǎo)入之后我們需要驗(yàn)證模塊導(dǎo)入是否成功,如果成功我們就可以進(jìn)行第二步了,找到我們要調(diào)用的方法。
?? ?(2)找到要調(diào)用的方法。
?? ?基本原理是這樣的,在C++中,如果我們有了模塊,就可以獲得模塊對(duì)應(yīng)的attr列表(dir(模塊名)),然后我們就可以利用我們所需要的方法名找到我們所需要的那個(gè)attr。找到之后我們要判斷時(shí)候找到成功,而且是否是可以調(diào)用的(callable)。這部分代碼如下:
// load the function PyObject *pyFunc = PyObject_GetAttrString(pyMod, methodname.c_str()); if(pyFunc && PyCallable_Check(pyFunc)) {// talk later!!! }??? 第一句話首先利用PyObject_GetAttrString找到我們所需的“方法”對(duì)象,然后檢測(cè)對(duì)象時(shí)候有效,是否是Callable的,如果這兩者檢測(cè)都成功了,那么ok,進(jìn)入下一步。
?? ?(3)構(gòu)造參數(shù)。
?? 方法有了,接下來(lái)就缺參數(shù)了。參數(shù)是以tuple的形式傳遞給方法的,所以我們需要一個(gè)PyObject*對(duì)象來(lái)充當(dāng)一個(gè)Tuple,利用PyTuple_New方法即可。這個(gè)方法的參數(shù)是tuple內(nèi)元素的個(gè)數(shù),我認(rèn)為可以理解為參數(shù)的個(gè)數(shù)。由于我的Python代碼只有一個(gè)參數(shù),所以這里我直接給1,其他情況可以根據(jù)參數(shù)的數(shù)量賦值。
?? ?然后解釋構(gòu)建參數(shù)了,根據(jù)參數(shù)的不用類(lèi)型,可以調(diào)用不同的方法。這里由于我在C++程序中的參數(shù)是字符串,在Python中的參數(shù)也是一個(gè)字符串,所以我使用PyString_FromString方法,其中Py是前綴,第一個(gè)String指的是在Python代碼中我們需要一個(gè)String,也就是目的參數(shù)類(lèi)型,而FromString顧名思義,就是C++中參數(shù)的類(lèi)型。這個(gè)方法接受一個(gè)參數(shù),也就是一個(gè)string,注意,這個(gè)string其實(shí)是char型數(shù)組。返回的結(jié)果是一個(gè)PyObject*。
?? ?最后一步是要把構(gòu)造的參數(shù)賦值給我們的Tuple,用PyTuple_SetItem方法就可以了。第一個(gè)參數(shù)是指向Tuple的PyObject指針,第二個(gè)參數(shù)是value在Tuple中的位置參數(shù),從0開(kāi)始,最后一個(gè)就是咱們的value了,這部分代碼如下:
PyObject *pyParams = PyTuple_New(1); PyObject *pyValue = PyString_FromString(expression.c_str()); PyTuple_SetItem(pyParams, 0, pyValue);??? (4)調(diào)用方法,取返回值。
?? ?好了,大功即將告成~最后一步當(dāng)然是調(diào)用方法了!利用PyObject_CallObject方法,第一個(gè)參數(shù)是指向函數(shù)的那個(gè)PyObject指針,第二個(gè)參數(shù)則是指向我們Tuple的那個(gè)PyObject指針。返回值仍然是PyObject指針對(duì)象。我們需要對(duì)它進(jìn)行解析,將它轉(zhuǎn)換成C++的類(lèi)型。這部分代碼如下:
// ok, call the function pyValue = PyObject_CallObject(pyFunc, pyParams); if(pyValue) {*result = PyFloat_AsDouble(pyValue); // result is a double varreturn true; }?? ? 好了,到這里調(diào)用的全部過(guò)程就完成了,其實(shí)還不賴。最后,為了方便大家理解,我把整個(gè)代碼貼了上來(lái)。
?? ?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
?? ?特別說(shuō)明,如果您是用Python安裝包的形式安裝的Python,那么這個(gè)程序只能以Release方式運(yùn)行!原因是安裝包版Python在libs文件夾下只有Release版的lib文件,而debug模式需要Python26_d.lib,也就是debug模式的。如果您在運(yùn)行時(shí)提示無(wú)法打開(kāi)python26_d.lib文件,那么請(qǐng)換成Release模式,或者下載Python源碼自己編譯,這樣就會(huì)生成_d的lib文件了!
?? ?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
?? ?由于我的水平實(shí)在有限,文中難免有錯(cuò)誤,希望各位看到之后留言指正!謝謝各位!
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)
總結(jié)
以上是生活随笔為你收集整理的C++调用Python函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python---PDF转JPG图片
- 下一篇: 阶乘的精确性