韋立梅 張淑榮
(廣東白云學(xué)院,廣東 廣州 510450)
隨著人工智能、大數(shù)據(jù)計(jì)算、系統(tǒng)運(yùn)維、網(wǎng)絡(luò)爬蟲(chóng)等技術(shù)的興起,Python作為目前最火的腳本語(yǔ)言,以其優(yōu)雅、簡(jiǎn)單、功能強(qiáng)大、開(kāi)發(fā)效率高、跨平臺(tái)等優(yōu)點(diǎn),被廣泛應(yīng)用于這些領(lǐng)域。C語(yǔ)言是一種編譯型語(yǔ)言,介于高級(jí)和低級(jí)語(yǔ)言之間,C程序的運(yùn)行必須要經(jīng)過(guò)編譯后,生成機(jī)器碼,然后再運(yùn)行,執(zhí)行速度快,但不能跨平臺(tái),目前主要用于操作系統(tǒng)、驅(qū)動(dòng)等底層的開(kāi)發(fā)。在實(shí)際開(kāi)發(fā)中,程序員往往會(huì)把程序中的性能瓶頸部分的實(shí)現(xiàn),以擴(kuò)展的方式用C程序來(lái)完成,而業(yè)務(wù)邏輯部分的實(shí)現(xiàn),則用Python這種高度集成、適合開(kāi)發(fā)大型項(xiàng)目的程序設(shè)計(jì)語(yǔ)言來(lái)完成,從而揚(yáng)長(zhǎng)避短,充分發(fā)揮出兩種語(yǔ)言的各自?xún)?yōu)勢(shì)。
(1)創(chuàng)建C源代碼(.c);
(2)把C源代碼打包成庫(kù)文件,也就是Python類(lèi)型適配,創(chuàng)建包裹函數(shù)(.c),包裝C代碼;
(3)編譯與測(cè)試。
(1)首先進(jìn)入U(xiǎn)buntu系統(tǒng),這里選擇在桌面下,新建一個(gè)文件夾pythonexc(以下創(chuàng)建的所有文件/夾都存放在該文件夾中,以方便管理),在該文件夾中創(chuàng)建一個(gè)名為MaxRun.c的源文件,實(shí)現(xiàn)定義一個(gè)求兩數(shù)最大值的函數(shù)maxn,并在主函數(shù)main中調(diào)用maxn這個(gè)函數(shù)。具體代碼如下:
(2)打開(kāi)Ubuntu中的終端窗口,使用cd命令進(jìn)入到桌面的pythonexc文件夾,然后使用gcc命令對(duì)C源文件編譯鏈接生成可執(zhí)行文件后,并運(yùn)行,看結(jié)果是否正確(在這步一定要保證所編寫(xiě)C代碼的正確性,以避免在Python中調(diào)試C的麻煩),編譯及輸出結(jié)果如下圖1所示,若正確,再將main函數(shù)重命名為如test(作為擴(kuò)展模塊,不能有main函數(shù),避免同名沖突),才能進(jìn)行下一步的操作。
圖1 gcc編譯及運(yùn)行后的輸出結(jié)果
(3)編寫(xiě)相應(yīng)的MaxRun.h頭文件,將定義的兩個(gè)函數(shù)maxn和test封裝到該頭文件里,在包裹模塊中需要調(diào)用它,代碼如下:
包裹模塊是C源代碼與Python解釋器之間進(jìn)行交互的橋梁。Python和C就是通過(guò)這個(gè)模塊,完成無(wú)縫適配的。在剛建好的pythonexc文件夾中,新建一個(gè)名字為MaxRun-Wrapper.c的源文件,作為MaxRun.c的包裹模塊。包裹模塊的代碼從上到下主要分為四部分:
第一部分代碼:包含Python.h等的頭文件。
Python.h頭文件一般存放在/usr/local/include/python2.x中,如果沒(méi)有可以在終端輸入命令:sudo apt-get install python-dev,安裝Python。在包裹函數(shù)的最上面加入如下四行C代碼:
第二部分代碼:為被包裹的C功能源代碼(MaxRun)中所寫(xiě)的每一個(gè)函數(shù)(maxn和test)增加一個(gè)靜態(tài)的PyObject*Module_func()的包裹函數(shù)。
包裹函數(shù)的作用就是在Python和C之間完成數(shù)據(jù)類(lèi)型的轉(zhuǎn)換。每個(gè)包裹函數(shù)的返回類(lèi)型為PyObject*,包裹函數(shù)的名字:模塊_函數(shù)名,如本例中應(yīng)為兩個(gè)函數(shù)命名為Max-Run_maxn和MaxRun_test,相應(yīng)的包裹函數(shù)代碼及功能說(shuō)明如圖2所示。
圖2 包裹函數(shù)代碼及功能說(shuō)明
第三部分:為模塊增加一個(gè)PyMethodDef Module-Methods[]的函數(shù)聲明數(shù)組。
創(chuàng)建完包裹函數(shù)后,需要用函數(shù)聲明數(shù)組的方式,把C功能源代碼中定義的函數(shù)與對(duì)應(yīng)的包裹函數(shù)一一列舉在數(shù)組中,以便Python解釋器能夠?qū)氩⒄{(diào)用這些函數(shù),最后的兩個(gè)NULL表示函數(shù)聲明結(jié)束。函數(shù)聲明數(shù)組代碼及格式說(shuō)明如圖3所示。
圖3 函數(shù)聲明數(shù)組的定義及格式說(shuō)明
第四部分:函數(shù)的初始化聲明。
初始化聲明中的代碼,會(huì)在包裹模塊被python導(dǎo)入時(shí)進(jìn)行調(diào)用,完成初始化C模塊以及這個(gè)模塊所包含的函數(shù)的作用。初始化聲明代碼如下:
至此,包裹模塊已經(jīng)全部完成,保存在MaxRunWrapper.c文件中。
為了讓Python的擴(kuò)展能夠被創(chuàng)建,需要把C源代碼、C源代碼頭文件、包裹模塊文件放在一起編譯,放到Python庫(kù),以便在其它Python文件中引入這個(gè)C擴(kuò)展。使用Python中的distutils包來(lái)編譯、安裝和分發(fā)這些模塊、擴(kuò)展和包。步驟如下:
(1)創(chuàng)建setup.py
在Python中是由setup函數(shù)完成編譯,應(yīng)為每一個(gè)擴(kuò)展創(chuàng)建一個(gè)Extension實(shí)例,我們這里只有一個(gè)擴(kuò)展。setup.py文件中的代碼及相關(guān)參數(shù)說(shuō)明如圖4所示。
圖4 setup.py文件代碼及相關(guān)參數(shù)說(shuō)明
(2)運(yùn)行setup.py編譯和連接C的擴(kuò)展代碼
在Ubuntu終端中執(zhí)行如下命令:python setup.py build。如果最終能生成一個(gè)*.so(這里是MaxRun.so)的動(dòng)態(tài)庫(kù)文件,表示編譯成功。該文件會(huì)被存放在pythonexc/bulid/lib.*目錄下。
(3)從Python中導(dǎo)入模塊和測(cè)試
在Ubuntu終端中執(zhí)行如下命令:sudo python setup.py install。安裝我們的setup.py文件,會(huì)把生成的動(dòng)態(tài)庫(kù)文件.so復(fù)制到Ubuntu的公共庫(kù)中,然后就可以用importMaxRun命令導(dǎo)入該擴(kuò)展模塊的動(dòng)態(tài)庫(kù)文件??梢栽趇python中以交互的方式測(cè)試,也可以新建一個(gè)python文件,用import命令導(dǎo)入該擴(kuò)展模塊,就可以無(wú)縫調(diào)用C擴(kuò)展模塊中的函數(shù)了。
以新建一個(gè)pytest.py文件為例,在其中導(dǎo)入C擴(kuò)展模塊,并調(diào)用其中的test函數(shù)、代碼及輸出效果如圖5所示。
圖5 測(cè)試文件代碼及輸出結(jié)果
在實(shí)際應(yīng)用開(kāi)發(fā)中,以下兩種情況可以考慮用C語(yǔ)言來(lái)擴(kuò)展Python:一是當(dāng)需要大規(guī)模的計(jì)算,遇到性能瓶頸的效率提升時(shí);二是需要保持源碼的私密性,如加密解密算法時(shí)。在用Python開(kāi)發(fā)項(xiàng)目的時(shí)候,如果能夠有效地借力C程序,可以大大提升程序的效率,是現(xiàn)在很多程序員經(jīng)常采用的編程方式。