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