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