加速程序開發(fā) Python整合C語言模塊
Python是一種用于快速開發(fā)軟件的編程語言,它的語法比較簡(jiǎn)單,易于掌握,但存在執(zhí)行速度慢的問題,并且在處理某些問題時(shí)存在不足,如對(duì)計(jì)算機(jī)硬件系統(tǒng)的訪問,對(duì)媒體文件的訪問等。而作為軟件開發(fā)的傳統(tǒng)編程語言C語言,卻能在這些問題上很好地彌補(bǔ)Python語言的不足。
51CTO推薦閱讀:專訪豆瓣網(wǎng)***架構(gòu)師洪強(qiáng)寧:Python,簡(jiǎn)單的力量
概覽
Python是一種用于快速開發(fā)軟件的編程語言,它的語法比較簡(jiǎn)單,易于掌握,但存在執(zhí)行速度慢的問題,并且在處理某些問題時(shí)存在不足,如對(duì)計(jì)算機(jī)硬件系統(tǒng)的訪問,對(duì)媒體文件的訪問等。而作為軟件開發(fā)的傳統(tǒng)編程語言——C語言,卻能在這些問題上很好地彌補(bǔ)Python語言的不足。因此,本文通過實(shí)例研究如何在Python程序中整合既有的C語言模塊,包括用C語言編寫的源程序和動(dòng)態(tài)鏈接庫等,從而充分發(fā)揮Python語言和C語言各自的優(yōu)勢(shì)。
Python語言的特點(diǎn)
Python作為一門程序開發(fā)語言,被越來越多地運(yùn)用到快速程序開發(fā)。Python是一種解釋型的,互動(dòng)的,面向?qū)ο蟮木幊陶Z言,它包含了模塊化的操作,異常處理,動(dòng)態(tài)資料形態(tài),以及類型的使用。它的語法表達(dá)優(yōu)美易讀,具有很多優(yōu)秀的腳本語言的特點(diǎn):解釋的,面向?qū)ο蟮模瑑?nèi)建的高級(jí)數(shù)據(jù)結(jié)構(gòu),支持模塊和包,支持多種平臺(tái),可擴(kuò)展。而且它還支持交互式方式運(yùn)行,圖形方式運(yùn)行。它擁有眾多的編程界面支持各種操作系統(tǒng)平臺(tái)以及眾多的各類函數(shù)庫,利用C和C++可以對(duì)它進(jìn)行擴(kuò)充。
C語言的特點(diǎn)
C語言作為最受人們歡迎的語言之一,有廣泛的發(fā)展基礎(chǔ)。簡(jiǎn)潔緊湊、靈活方便,功能強(qiáng)大是其特點(diǎn)。另外,C語言是一門中級(jí)語言。它把高級(jí)語言的基本結(jié)構(gòu)和語句與低級(jí)語言的實(shí)用性結(jié)合起來。由于可以直接訪問物理地址,可以方便的對(duì)硬件進(jìn)行操作。因此,很多的系統(tǒng)軟件都是由C語言編寫。
Python語言與C語言的交互
為了節(jié)省軟件開發(fā)成本,軟件開發(fā)人員希望能夠縮短的軟件的開發(fā)時(shí)間,希望能夠在短時(shí)間內(nèi)開發(fā)出穩(wěn)定的產(chǎn)品。Python功能強(qiáng)大,簡(jiǎn)單易用,能夠快速開發(fā)應(yīng)用軟件。但是由于Python自身執(zhí)行速度的局限性,對(duì)性能要求比較高的模塊需要使用效率更高的程序語言進(jìn)行開發(fā)。
例如C語言,系統(tǒng)的其他模塊運(yùn)用Python進(jìn)行快速開發(fā),***將C語言開發(fā)的模塊與Python開發(fā)的模塊進(jìn)行整合。在此背景下,基于Python語言與C語言的各自特點(diǎn),用C語言來擴(kuò)展現(xiàn)有的Python程序,顯得很有意義。本文首先介紹幾種常用的整合Python程序與C語言程序的方法,***給出相應(yīng)的實(shí)例。
利用ctypes模塊整合Python程序和C程序
ctypes模塊
ctypes是Python的一個(gè)標(biāo)準(zhǔn)模塊,它包含在Python2.3及以上的版本里。ctypes是一個(gè)Python的高級(jí)外部函數(shù)接口,它使得Python程序可以調(diào)用C語言編譯的靜態(tài)鏈接庫和動(dòng)態(tài)鏈接庫。運(yùn)用ctypes模塊,能夠在Python源程序中創(chuàng)建,訪問和操作簡(jiǎn)單的或復(fù)雜的C語言數(shù)據(jù)類型。
最為重要的是ctypes模塊能夠在多個(gè)平臺(tái)上工作,包括Windows,WindowsCE,MacOSX,Linux,Solaris,F(xiàn)reeBSD,OpenBSD。接下來通過幾個(gè)簡(jiǎn)單的例子來看一下ctypes模塊如何整合Python程序和C程序。
#p#
源代碼層面上的整合
利用Python本身提供的ctypes模塊可以使Python語言和C語言在源代碼層面上進(jìn)行整合。本節(jié)介紹了如何通過使用ctypes庫,在Python程序中可以定義類似C語言的變量。下表列出了ctypes變量類型,C語言變量類型和Python語言變量類型之間的關(guān)系:
表1.ctypes,c語言和Python語言變量類型關(guān)系
表1中的***列是在ctypes庫中定義的變量類型,第二列是C語言定義的變量類型,第三列是Python語言在不使用ctypes時(shí)定義的變量類型。舉例:
- 清單1.ctypes簡(jiǎn)單使用
- >>>fromctypesimport*#導(dǎo)入ctypes庫中所有模塊
- >>>i=c_int(45)#定義一個(gè)int型變量,值為45
- >>>i.value#打印變量的值
- 45
- >>>i.value=56#改變?cè)撟兞康闹禐?6
- >>>i.value#打印變量的新值
- 56
從下面的例子可以更明顯地看出ctypes里的變量類型和C語言變量類型的相似性:
- 清單2.ctypes使用C語言變量
- >>>p=create_string_buffer(10)#定義一個(gè)可變字符串變量,長度為10
- >>>p.raw#初始值是全0,即C語言中的字符串結(jié)束符’\0’
- '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
- >>>p.value="Student"#字符串賦值
- >>>p.raw#后三個(gè)字符仍是’\0’
- 'Student\x00\x00\x00'
- >>>p.value="Big"#再次賦值
- >>>p.raw#只有前三個(gè)字符被修改,第四個(gè)字符被修改為’\0’
- 'Big\x00ent\x00\x00\x00'
下面例子說明了指針操作:
- 清單3.ctypes使用C語言指針
- >>>i=c_int(999)#定義int類型變量i,值為999
- >>>pi=pointer(i)#定義指針,指向變量i
- >>>pi.contents#打印指針?biāo)傅膬?nèi)容
- c_long(999)
- >>>pi.contents=c_long(1000)#通過指針改變變量i的值
- >>>pi.contents#打印指針?biāo)傅膬?nèi)容
- c_long(1000)
下面例子說明了結(jié)構(gòu)和數(shù)組的操作:
- 清單4.ctypes使用C語言數(shù)組和結(jié)構(gòu)體
- >>>classPOINT(Structure):#定義一個(gè)結(jié)構(gòu),內(nèi)含兩個(gè)成員變量x,y,均為int型
- ..._fields_=[("x",c_int),
- ...("y",c_int)]
- ...
- >>>point=POINT(2,5)#定義一個(gè)POINT類型的變量,初始值為x=2,y=5
- >>>printpoint.x,point.y#打印變量
- 25
- >>>point=POINT(y=5)#重新定義一個(gè)POINT類型變量,x取默認(rèn)值
- >>>printpoint.x,point.y#打印變量
- 05
- >>>POINTPOINT_ARRAY=POINT*3#定義POINT_ARRAY為POINT的數(shù)組類型
- #定義一個(gè)POINT數(shù)組,內(nèi)含三個(gè)POINT變量
- >>>pa=POINT_ARRAY(POINT(7,7),POINT(8,8),POINT(9,9))
- >>>forpinpa:printp.x,p.y#打印POINT數(shù)組中每個(gè)成員的值
- ...
- 77
- 88
- 99
#p#
Python訪問C語言dll
通過ctypes模塊,Python程序可以訪問C語言編譯的dll,本節(jié)通過一個(gè)簡(jiǎn)單的例子,Python程序helloworld.py中調(diào)用some.dll中的helloworld函數(shù),來介紹Python程序如何調(diào)用windows平臺(tái)上的dll。
導(dǎo)入動(dòng)態(tài)鏈接庫
- 清單5.ctypes導(dǎo)入dll
- fromctypesimportwindll#首先導(dǎo)入ctypes模塊的windll子模塊
- somelibc=windll.LoadLibrary(some.dll)#使用windll模塊的LoadLibrary導(dǎo)入動(dòng)態(tài)鏈接庫
訪問動(dòng)態(tài)鏈接庫中的函數(shù)
- 清單6.ctypes使用dll中的函數(shù)
- somelibc.helloworld()#這樣就可以得到some.dll的helloworld的返回值
整個(gè)helloworld.py是這樣的:
- 清單7.Pythonhellpworld代碼
- fromctypesimportwindll
- defcallc():
- #loadthesome.dll
- somelibc=windll.LoadLibrary(some.dll)
- printsomelibc.helloworld()
- if__name__==“__main__”:
- callc()
在命令行運(yùn)行helloworld.py,在console上可以看到some.dll中helloworld的輸出。
- 清單8.PythonhellpworldWindowscommandconsole運(yùn)行輸出
- C:\>pythonC:\python\test\helloworld.py
- HelloWorld!Justasimpletest.
Python調(diào)用C語言so
通過ctypes模塊,Python程序也可以訪問C語言編譯的so文件。與Python調(diào)用C的dll的方法基本相同,本節(jié)通過一個(gè)簡(jiǎn)單的例子,Python程序helloworld.py中調(diào)用some.so中的helloworld函數(shù),來介紹Python程序如何調(diào)用linux平臺(tái)上的so。
導(dǎo)入動(dòng)態(tài)鏈接庫
- 清單9.ctypes導(dǎo)入so
- fromctypesimportcdll
- #首先導(dǎo)入ctypes模塊的cdll子模塊,注意linux平臺(tái)上使用cdll的,而不是windll。
- somelibc=cdll.LoadLibrary(“./some.so”)
- #使用cdll模塊的LoadLibrary導(dǎo)入動(dòng)態(tài)鏈接庫
訪問動(dòng)態(tài)鏈接庫中的函數(shù)
- 清單10.ctypes使用so中的函數(shù)
- somelibc.helloworld()#使用方法與windows平臺(tái)上是一樣的
整個(gè)helloworld.py是這樣的:
- 清單11.Pythonhelloworld代碼
- fromctypesimportcdll
- defcallc():
- #loadthesome.so
- somelibc=cdll.LoadLibrary(some.so)
- printsomelibc.helloworld()
- if__name__==“__main__”:
- callc()
在命令行運(yùn)行helloworld.py,在linux標(biāo)準(zhǔn)輸出上可以看到some.so中helloworld的輸出。
- 清單12.PythonhellpworldLinuxshell運(yùn)行輸出
- [root@linux-790t]python./helloworld.py
- HelloWorld!Justasimpletest.
#p#
Python程序和C程序整合實(shí)例
以下我們舉例用Python來實(shí)現(xiàn)一個(gè)小工具,用來實(shí)現(xiàn)hash算法,查看文件的校驗(yàn)和(MD5,CRC,SHA1等等)。通過查看文件的校驗(yàn)和,可以知道文件在傳輸過程中是否被破壞或篡改。
Hash,一般翻譯做“散列”,也有直接音譯為"哈希"的,就是把任意長度的輸入(又叫做預(yù)映射,pre-image),通過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉(zhuǎn)換是一種壓縮映射,也就是,散列值的空間通常遠(yuǎn)小于輸入的空間,不同的輸入可能會(huì)散列成相同的輸出,而不可能從散列值來唯一的確定輸入值。簡(jiǎn)單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數(shù)。
由于相對(duì)C語言來說,Python的運(yùn)行效率較低,因此我們的Python小工具利用一個(gè)已有的C語言的動(dòng)態(tài)鏈接庫(hashtcalc.dll)來實(shí)現(xiàn)我們的程序。本例中,我們運(yùn)用wxPython編寫簡(jiǎn)單的GUI界面,通過python調(diào)用hashtcalc.dll的接口計(jì)算文件的校驗(yàn)和,然后輸出在界面上。
架構(gòu)圖
hashcalc.dll接口描述
函數(shù)名:calc_CRC32
函數(shù):char*calc_CRC32(char*filename);
參數(shù):文件名
返回值:字符串
說明:該函數(shù)對(duì)輸入的文件內(nèi)容進(jìn)行計(jì)算,并且返回它的CRC32
函數(shù)名:calc_MD5
函數(shù):char*calc_MD5(char*filename);
參數(shù):文件名
返回值:字符串
說明:該函數(shù)對(duì)輸入的文件內(nèi)容進(jìn)行計(jì)算,并且返回它的MD5
函數(shù)名:calc_SHA1
函數(shù):char*calc_SHA1(char*filename);
參數(shù):文件名
返回值:字符串
說明:該函數(shù)對(duì)輸入的文件內(nèi)容進(jìn)行計(jì)算,并且返回它的SHA1
HashcalcAdapter代碼
HashcalcAdapter.py實(shí)現(xiàn)了一個(gè)python的classHashcalcAdapter,HashcalcAdapter對(duì)hashtcalc.dl的C語言接口進(jìn)行了封裝,使得其他python模塊可以直接通過HashcalcAdapter使用hashtcalc.dll中實(shí)現(xiàn)的hash算法。具體的代碼如下:
- 清單13.HashcalcAdapter.py代碼
- fromctypesimportwindll
- fromctypesimport*
- classHashcalcAdapter(object):
- def__init__(self,dllpath):
- self._dllpath=dllpath
- self._libc=windll.LoadLibrary(self._dllpath)
- defcalc_CRC32(self,filename):
- new_filename=c_char_p(filename)
- returnself._libc.calc_CRC32(new_filename)
- defcalc_MD5(self,filename):
- new_filename=c_char_p(filename)
- returnself._libc.calc_MD5(new_filename)
- defcalc_SHA1(self,filename):
- new_filename=c_char_p(filename)
- returnself._libc.calc_SHA1(new_filename)
運(yùn)行界面
總結(jié)
在軟件開發(fā)過程中同時(shí)運(yùn)用Python語言和C語言,既能夠在加快開發(fā)速度的同時(shí),也能夠保證軟件的運(yùn)行性能。
【編輯推薦】