你試過(guò)C語(yǔ)言和Python一起混合編程嗎??jī)烧呦嗉硬皇且呀?jīng)無(wú)敵了!
C語(yǔ)言是編程語(yǔ)言的祖母,但是隨著一代一代的編程語(yǔ)言長(zhǎng)大,所以祖母也是會(huì)拍在沙灘上的,很多小小伙伴應(yīng)該都會(huì)學(xué)過(guò)或者了解C語(yǔ)言,因?yàn)檐浖档臅?huì)教嘛,但是Python我想很多人都沒(méi)學(xué)過(guò),下面小編給大家介紹下,C語(yǔ)言和Python一起混合編程會(huì)產(chǎn)生什么不一樣的火花吧!

在Mac OS X 下的編譯命令同上

產(chǎn)生可執(zhí)行文件后,直接運(yùn)行,結(jié)果為輸出
1 2 | Hello Python! |
Python庫(kù)函數(shù)PyRun_SimpleString可以執(zhí)行字符串形式的Python代碼。
雖然非常簡(jiǎn)單,但這段代碼除了能用C語(yǔ)言動(dòng)態(tài)生成一些Python代碼之外,并沒(méi)有什么用處。我們需要的是C語(yǔ)言的數(shù)據(jù)結(jié)構(gòu)能夠和Python交互。
下面舉個(gè)例子,比如說(shuō),有一天我們用Python寫(xiě)了一個(gè)功能特別強(qiáng)大的函數(shù):


從上述代碼可以窺見(jiàn)Python內(nèi)部運(yùn)行的方式:
所有Python元素,module、function、tuple、string等等,實(shí)際上都是PyObject。C語(yǔ)言里操縱它們,一律使用PyObject *。
Python的類(lèi)型與C語(yǔ)言類(lèi)型可以相互轉(zhuǎn)換。Python類(lèi)型XXX轉(zhuǎn)換為C語(yǔ)言類(lèi)型YYY要使用PyXXXAsYYY函數(shù);C類(lèi)型YYY轉(zhuǎn)換為Python類(lèi)型XXX要使用PyXXXFromYYY函數(shù)。
也可以創(chuàng)建Python類(lèi)型的變量,使用PyXXX_New可以創(chuàng)建類(lèi)型為XXX的變量。
若a是Tuple,則a[i] = b對(duì)應(yīng)于 PyTupleSetItem(a,i,b),有理由相信還有一個(gè)函數(shù)PyTupleGetItem完成取得某一項(xiàng)的值。
不僅Python語(yǔ)言很優(yōu)雅,Python的庫(kù)函數(shù)API也非常優(yōu)雅。
現(xiàn)在我們得到了一個(gè)C語(yǔ)言的函數(shù)了,可以寫(xiě)一個(gè)main測(cè)試它

編譯的方式就用本節(jié)開(kāi)頭使用的方法。
在Linux/Mac OSX運(yùn)行此示例之前,可能先需要設(shè)置環(huán)境變量:
bash:
1 2 | export PYTHONPATH=.:$PYTHONPATH |
csh:
1 2 | setenv PYTHONPATH.:$PYTHONPATH |
2 Python 調(diào)用 C/C++(基礎(chǔ)篇)
這種做法稱(chēng)為Python擴(kuò)展。
比如說(shuō),我們有一個(gè)功能強(qiáng)大的C函數(shù)


除了功能強(qiáng)大的函數(shù)great_function外,這個(gè)文件中還有以下部分:
包裹函數(shù)greatfunction。它負(fù)責(zé)將Python的參數(shù)轉(zhuǎn)化為C的參數(shù)(PyArgParseTuple),調(diào)用實(shí)際的greatfunction,并處理great_function的返回值,最終返回給Python環(huán)境。
導(dǎo)出表GreateModuleMethods。它負(fù)責(zé)告訴Python這個(gè)模塊里有哪些函數(shù)可以被Python調(diào)用。導(dǎo)出表的名字可以隨便起,每一項(xiàng)有4個(gè)參數(shù):第一個(gè)參數(shù)是提供給Python環(huán)境的函數(shù)名稱(chēng),第二個(gè)參數(shù)是greatfunction,即包裹函數(shù)。第三個(gè)參數(shù)的含義是參數(shù)變長(zhǎng),第四個(gè)參數(shù)是一個(gè)說(shuō)明性的字符串。導(dǎo)出表總是以{NULL, NULL, 0, NULL}結(jié)束。
導(dǎo)出函數(shù)initgreat_module。這個(gè)的名字不是任取的,是你的module名稱(chēng)添加前綴init。導(dǎo)出函數(shù)中將模塊名稱(chēng)與導(dǎo)出表進(jìn)行連接。
在Windows下面,在Visual Studio命令提示符下編譯這個(gè)文件的命令是

本部分參考資料
《Python源碼剖析-深度探索動(dòng)態(tài)語(yǔ)言核心技術(shù)》是系統(tǒng)介紹CPython實(shí)現(xiàn)以及運(yùn)行原理的優(yōu)秀教程。
Python 官方文檔的這一章詳細(xì)介紹了C/C++與Python的雙向互動(dòng)Extending and Embedding the Python Interpreter _ _
關(guān)于編譯環(huán)境,本文所述方法僅為出示原理所用。規(guī)范的方式如下:3. Building C and C++ Extensions with distutils _ _
作為字典使用的官方參考文檔Python/C API Reference Manual _ _

這其中有非Python關(guān)鍵字cdef和public。這些關(guān)鍵字屬于Cython。由于我們需要在C語(yǔ)言中使用“編譯好的Python代碼”,所以得讓great_function從外面變得可見(jiàn),方法就以“public”修飾。而cdef類(lèi)似于Python的def,只有使用cdef才可以使用Cython的關(guān)鍵字public。
這個(gè)函數(shù)中其他的部分與正常的Python代碼是一樣的。
接下來(lái)編譯 great_module.pyx


編譯命令和第一部分相同:
在Windows下編譯命令為


在Visual Studio命令提示符下編譯:
1 2 | cl/LD dllmain.cgreat_module.c-IC:Python27includeC:Python27libspython27.lib |
會(huì)得到一個(gè)dllmain.dll。我們?cè)贓xcel里面使用它,沒(méi)錯(cuò),傳說(shuō)中的Excel與Python混合編程:

參考資料:Cython的官方文檔,質(zhì)量非常高:


接下來(lái)使用SWIG將這個(gè)配置文件編譯為所謂Python Module Wrapper
1 2 | swig-python mymodule.i |
得到一個(gè) mymodule_wrap.c和一個(gè)mymodule.py。把它編譯為Python擴(kuò)展:
Windows:
1 2 | cl/LD mymodule_wrap.c/o_mymodule.pyd-IC:Python27includeC:Python27libspython27.lib |
Linux:
1 2 | gcc-fPIC-shared mymodule_wrap.c-o_mymodule.so-I/usr/include/python2.7/-lpython2.7 |
注意輸出文件名前面要加一個(gè)下劃線。
現(xiàn)在可以立即在Python下使用這個(gè)module了:

換句話說(shuō),SWIG自動(dòng)完成了諸如Python類(lèi)型轉(zhuǎn)換、module初始化、導(dǎo)出代碼表生成的諸多工作。
對(duì)于C++,SWIG也可以應(yīng)對(duì)。例如以下代碼有C++類(lèi)的定義:


寫(xiě)在最后:
由于CPython自身的結(jié)構(gòu)設(shè)計(jì)合理,使得Python的C/C++擴(kuò)展非常容易。如果打算快速完成任務(wù),Cython(C/C++調(diào)用Python)和SWIG(Python調(diào)用C/C++)是很不錯(cuò)的選擇。但是,一旦涉及到比較復(fù)雜的轉(zhuǎn)換任務(wù),無(wú)論是繼續(xù)使用Cython還是SWIG,仍然需要學(xué)習(xí)Python源代碼。

