Python中擴充套件C語言加快執行速度的實現方法

2021-10-03 14:06:59 字數 4178 閱讀 9795

當我們提到一門程式語言的效率時,通常包含了開發效率和執行效率這兩層意思。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",n,id(n))

if n<=4:up_add_down(n+1)

print("level %d: n location %p\n",n,id(n))

return

列印返回:
('level %d: n location %p\n', 0, 144136380) ('level %d: n location %p\n', 1, 144136368) 

('level %d: n location %p\n', 2, 144136356) ('level %d: n location %p\n', 3, 144136344)

('level %d: n location %p\n', 4, 144136332) ('level %d: n location %p\n', 5, 144136320)

('level %d: n location %p\n', 5, 144136320) ('level %d: n location %p\n', 4, 144136332)

('level %d: n location %p\n', 3, 144136344) ('level %d: n location %p\n', 2, 144136356)

('level %d: n location %p\n', 1, 144136368) ('level %d: n location %p\n', 0, 144136380)

python在設計之初就考慮到通過足夠抽象的機制讓c和c++之類的編譯型的語言匯入到python指令碼**中,在python的官方**上也找到了擴充套件和嵌入python直譯器對應的方法。鏈結為:

這裡介紹下如何將c編寫的函式擴充套件至python直譯器中。

#include unsigned long long factorial(int n)

pyobject* wrap_fact(pyobject* self,pyobject* args)

,//meth_noargs無需引數/meth_varargs需要引數;,};

注:初始化函式名必須為initmodule_name這樣的格式

(2)安裝python-dev包含python.h標頭檔案

安裝命令:sudo apt-get python-dev
注:雖然已經安裝了python-dev,但編譯時仍然提示「python.h:沒有那個檔案或目錄」,需要通過gcc的-i dir選項在標頭檔案的搜尋路徑列表中新增dir目錄

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)

(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庫使用方便,特別適合應用在第三方封裝**,提供動態鏈結庫和呼叫文件的場合。

C語言擴充套件Python

python具有很好的開發靈活性,最大的特點是c語言可以對python進行擴充套件,目前工作中正在進行相關的開發,第一篇文章作為基礎.實現c函式,用python api封裝,實現倆個功能,1.say hello,列印hello world 2.calc pv,做加法用算.以下為使用方法 01pyth...

使用c語言擴充套件python

背景 我們工作中可能有時需要和第三方對接,而第三方提供的sdk可能是c語言或者c 編寫的,而我們程式使用的語言是python,這時候就需要使用python呼叫c庫 參考官方文件 編寫python擴充套件分三步 第一步 建立應用 使用c語言編寫具體的功能 如果sdk已經提供了並且不需要我們整合,則跳過...

使用C語言擴充套件Python 四

首先,遵循 測試先行 的原則,先來看我們改造後的python這一端,你可以每次讀取音訊原始檔的乙個資料塊,將其轉遞給encoder物件的encode方法,這樣無論你的原始檔是何種格式,你都可以在encoder中進行自由的控制,示例 如下 import clame inbufsize 4096 if ...