摘要:通過執行時間對比可以發現調用函數來擴展功能可以大大提高執行速度,而自帶的庫由于在源生代碼上進行封裝,執行時間會高于源生代碼擴展方式,但庫使用方便,特別適合應用在第三方封裝代碼,提供動態鏈接庫和調用文檔的場合。
前言
當我們提到一門編程語言的效率時,通常包含了開發效率和運行效率這兩層意思。Python作為一門高級語言,它功能強大,易于掌握,能夠快速的開發軟件,“life?is?short,we?use?python!”,想必這些優點是毋庸置疑的,但是作為一門解釋性語言,執行速度的局限性導致在處理某些高頻任務時存在不足。
由于Python本身由C語言實現的,開發性能要求較高的程序模塊可以通過擴展運行效率更高的C語言來彌補自身的弱點。另外有些算法已經有開源的C庫,那么也沒必要用Python重寫一份,只需要通過Python進行C庫的調用即可。
本文通過實例介紹如何在Python 程序中整合既有的C語言模塊,從而充分發揮Python 語言和 C 語言各自的優勢。
使用Python編寫一個遞歸函數和循環函數,應用Python的計時庫timeit測試函數執行10000次所需要的時間分別為57ms和41ms。
實現代碼如下:
from timeit import timeit? def factorial(n): ?? ?if n<2:return 1 return factorial(n-1)*n def rooporial(n): ?? ?if n<2:return 1 ?? ?ans = 1 ?? ?for i in range(1,n+1): ?? ??? ?ans *=i ?? ?return ans if __name__ == "__main__": print "factorial",factorial(20),timeit("factorial(20)","from __main__ import factorial",number=10000) #timeit(‘函數名’,‘運行環境’,number=運行次數) print "rooporial",rooporial(20),timeit("rooporial(20)","from __main__ import rooporial",number=10000)
打印返回:
factorial 2432902008176640000 0.0578598976135 factorial 2432902008176640000 0.0410023010987
當然遞歸方法使程序的結構簡潔,但由于它逐層深入調用的機制使得執行效率不如循環,以下的測試可以發現每一次遞歸是新一次的函數調用,會產生新的局部變量,增加了執行時間。但是即使使用For循環實現也需要41ms時間,接下來我們嘗試更快的實現方式。
測試代碼如下:
def up_add_down(n): ?? ?print("level %d: n location %p ",n,id(n)) ?? ?if n<=4:up_add_down(n+1) ?? ?print("level %d: n location %p ",n,id(n)) ?? ?return
打印返回:
("level %d: n location %p ", 0, 144136380) ("level %d: n location %p ", 1, 144136368) ("level %d: n location %p ", 2, 144136356) ("level %d: n location %p ", 3, 144136344) ("level %d: n location %p ", 4, 144136332) ("level %d: n location %p ", 5, 144136320) ("level %d: n location %p ", 5, 144136320) ("level %d: n location %p ", 4, 144136332) ("level %d: n location %p ", 3, 144136344) ("level %d: n location %p ", 2, 144136356) ("level %d: n location %p ", 1, 144136368) ("level %d: n location %p ", 0, 144136380)
Python在設計之初就考慮到通過足夠抽象的機制讓C和C++之類的編譯型的語言導入到Python腳本代碼中,在Python的官方網站上也找到了擴展和嵌入Python解釋器對應的方法。鏈接為:https://docs.python.org/2.7/e...。這里介紹下如何將C編寫的函數擴展至Python解釋器中。
(1)將C編寫的遞歸函數存為wrapper.c,作為Python的擴展庫。同時需要對C函數增加一個型如PyObject* Module_func()的封裝接口,該接口用于Python解釋器的交互。將封裝接口加入至型如PyMethodDef ModuleMethods[]的數組中,Python解釋器能夠從數組中導入并調用到封裝接口。最后是實現對擴展庫的初始化函數,調用Py_InitModule()函數,把擴展庫和ModuleMethods[]數組的名字傳遞進去,以便于解釋器能正確的調用庫中的函數。
wrapper.c實現代碼如下:
#includeunsigned long long factorial(int n) { ?? ?if(n<2)return 1; ?? ?return factorial(n-1)*n; } PyObject* wrap_fact(PyObject* self,PyObject* args) { ?? ?int n; ?? ?unsigned long long? result; ?? ? ?? ?if(!PyArg_ParseTuple(args,"i:fact",&n))return NULL;//i 整形 ?? ?result = factorial(n); ?? ?return Py_BuildValue("L",result);//L longlong型 } static PyMethodDef wrapperMethods[] = { ?? ?{"fact",wrap_fact,METH_VARARGS,"Caculate N!"},//METH_NOARGS無需參數/METH_VARARGS需要參數; ?? ?{NULL,NULL}, }; int initwrapper() { ?? ?PyObject* m; ?? ?m = Py_InitModule("wrapper",wrapperMethods);//參數:擴展庫名稱/庫所包含的方法 ?? ?return 0; }
注:初始化函數名必須為initmodule_name這樣的格式
(2)安裝python-dev包含Python.h頭文件
安裝命令:sudo apt-get python-dev
(3)在linux環境下wrapper.c編譯成動態鏈接庫wrapper.so
編譯命令:gcc wrapper.c -fPIC -shared -o wrapper.so -I/usr/include/python2.7
注:雖然已經安裝了python-dev,但編譯時仍然提示“Python.h:沒有那個文件或目錄”,需要通過gcc的-I dir選項在頭文件的搜索路徑列表中添加dir目錄
(4)Python文件中import wrapper導入動態鏈接庫,在import語句導入庫時會執行初始化函數
(5)Python文件中wrapper.fact()方式對C函數調用時,封裝函數wrap_fact()先會被調用,封裝函數接收到一個Python整形對象,PyArg_ParseTuple將Python整形對象轉為C整形參數,然后調用C的factorial()函數并將C整數參數傳入,經過運算后得到一個C長整形的返回值,Py_BuildValue把C長整形返回值轉為Python的長整形對象作為最終整個函數調用的結果。
(6)timeit測試函數wrapper.fact(20)執行10000次所的時間只需要5.9ms。
factorial_rc 2432902008176640000 0.00598216056824
Python內建ctypes庫使用了各個平臺動態加載動態鏈接庫的方法,并在Python源生代碼基礎上通過類型映射方式將Python與二進制動態鏈接庫相關聯,實現Python與C語言的混合編程,可以很方便地調用C語言動態鏈接庫中的函數。(ctypes源碼路徑:/Modules/_ctypes/_ctypes.c、/Modules/_ctypes/callproc.c)
(1)將C編寫的遞歸函數存為a.c,不需要對C函數經過Python接口封裝
#include??? #include? ?? unsigned long long factorial(int n) { ?? ?if(n<2)return 1; ?? ?return factorial(n-1)*n; }
(2)在linux環境下a.c編譯成動態鏈接庫a.so
編譯命令:gcc a.c -fPIC -shared -o a.so
(3)Python文件中調用動態鏈接庫a.so,在Windows平臺下,最終調用的是Windows API中LoadLibrary函數和GetProcAddress函數,在Linux和Mac OS X平臺下,最終調用的是Posix標準中的dlopen和dlsym函數。ctypes庫內部完成PyObject* 和C types之間的類型映射,使用時只需設置調用參數和返回值的轉換類型即可。
from ctypes import cdll from ctypes import * libb = cdll.LoadLibrary("./a.so") def factorial_c(n): ?? ?return libb.factorial(n) if __name__ == "__main__": libb.factorial.restype=c_ulonglong#返回類型 libb.factorial.argtype=c_int#傳入類型 print "factorial_c",factorial_c(20),timeit("factorial_c(20)","from __main__ import factorial_c",number=10000)
(4)timeit測試函數libb.factorial(20)執行10000次所的時間需要8.5ms。
factorial_c 2432902008176640000 0.00857210159302
如下圖所示,factorial、factorial_c、factorial_rc分別為Python腳本、ctypes 庫和Python源生代碼擴展方式來實現函數的執行時間。通過執行時間對比可以發現調用C函數來擴展Python功能可以大大提高執行速度,而Python自帶的ctypes庫由于在源生代碼上進行封裝,執行時間會高于源生代碼擴展方式,但ctypes庫使用方便,特別適合應用在第三方封裝代碼,提供動態鏈接庫和調用文檔的場合。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41314.html
摘要:的批評者聲稱性能低效執行緩慢,但實際上并非如此嘗試以下個小技巧,可以加快應用程序。使用或者機器語言擴展包來執行關鍵任務能極大改善性能。但是如果你把求值的結果放入一個變量中,就能提高程序的性能。 Python是一門非常酷的語言,因為很少的Python代碼可以在短時間內做很多事情,并且,Python很容易就能支持多任務和多重處理。 Python的批評者聲稱Python性能低效、執行緩慢,...
摘要:但是語言并沒有成功,究其原因,認為是其非開標識放造成的。已經成為最受歡迎的程序設計語言之一。年月,該語言作者在郵件列表上宣布將于年月日終止支持。其中很重要的一項就是的縮進規則。設計定位的設計哲學是優雅明確簡單。 文本標簽 換行標簽 -- br 是單標簽,意味著它沒有結束標簽。起強制換行作用 段落中的文字段落中的文字段落中的文字 水平分割線 -- hr 與br相同,也是單標簽。可用來區分...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
閱讀 2529·2023-04-25 14:54
閱讀 599·2021-11-24 09:39
閱讀 1808·2021-10-26 09:51
閱讀 3853·2021-08-21 14:10
閱讀 3483·2021-08-19 11:13
閱讀 2694·2019-08-30 14:23
閱讀 1808·2019-08-29 16:28
閱讀 3356·2019-08-23 13:45