淺談Python程式與C++程式的聯合使用_python

來源:互聯網
上載者:User

作為Python程式員,應該能夠正視Python的優點與缺點。眾所周之,Python的運行速度是很慢的,特別是大資料量的運算時,Python會慢得讓人難以忍受。對於這種情況,“專業”的解決方案是用上numpy或者opencl。不過有時候為了一點小功能用上這種重型的解決方案很不划算,或者有時候想要實現的操作在numpy裡面沒有,需要我們自己用C語言來編寫。總之,我們使用Python與C++的混合編程能夠加快程式熱點的運算速度。

首先要提醒大家注意的是,在考慮聯合編程之前一定要找到程式啟動並執行熱點。簡單一點地,使用標準庫的profile或者cProfile模組找到最消耗CPU的位置,如果這個位置只簡單的消耗IO時間,通常換成C++程式的意義也不會很大,此時做聯合編程可能是事倍功半,起不到多大的效果。

還有些情況,Python程式員們想要使用作業系統或者外部模組提供的函數。這些模組一般是為C/C++程式員提供的。這時候也是Python與C++聯合編程的用武之地。

Python語言可以說是最好的膠水語言。僅就與C++聯合編程這個問題來講,依使用難度與功能來排列,Python社區提供了以下幾種解決方案:

1.使用標準庫ctypes直接調用C/C++編寫的動態連結程式庫。這是最簡單易用的方案。C/C++程式員使用自己的豐富的經驗,把預定的功能實現為動態連結程式庫。而Python程式員只要知道這些動態連結程式庫函數的名稱、參數類型與傳回值類型就能簡單地調用它。當你傳入參數時,ctypes模組會自動地把Python的對象成為C/C++所對應的參數類型。比如以下調用Windows的API:

  #定義參數類型與函數名稱  from ctypes.wintypes import UINT, DWORD  GetLastInputInfo = ctypes.windll.user32.GetLastInputInfo  class LASTINPUTINFO(ctypes.Structure):    _fields_ = [("cbSize", UINT),         ("dwTime", DWORD)]  #開始調用DLL匯出的函數  def getLastInputTime_nt():    info = LASTINPUTINFO()    info.cbSize = ctypes.sizeof(info)    info.dwTime = 0    if not GetLastInputInfo(ctypes.byref(info)):      raise WindowsError("")    return info.dwTime

    在這裡展示了如何構造Windows的API所需要的結構體,如何填充結構體並分析傳回值。

    ctypes還能將Python函數提供給C/C++代碼作為回呼函數。

    與其它解決方案相比。ctypes不需要程式員熟悉C/C++語言,不需要安裝一個C/C++的編譯器,它通過作業系統的介面直接操作C/C++代碼。而且ctypes是標準庫的一部分,只要安裝了Python就可以直接使用。這幾個原因使得它深受Python程式員的喜愛。

    而它的劣勢呢。首先,ctypes不能簡單調用C++程式,因為C++在編譯的時候使用了name mangling這個技術來實現函數的重載。C++會自動地為類的成員函數加上類名首碼。所以,C++程式員需要以C語言的呼叫慣例來提供介面,沒有類,沒有重載函數,沒有模板,沒有C++異常。不能直接調用現有的C++代碼可能是這個方案最大的缺點。

    另外,對於list, set之類的資料類型,ctypes不能識別並自動地在Python與C/C++資料類型之間轉換。C/C++部分不能識別Python資料類型,這時候只能用Python語言來編寫轉碼。如果資料量較大,或者調用很頻繁,轉碼反而會浪費很多的資源。這或許是ctypes的另一個劣勢之一了。

2.如果你使用的是Jython或者IronPython的話,它們也提供了類似於ctypes之類的模組,能夠直接存取Java或者.Net語言編寫的模組。其優勢與劣勢大致與ctypes相似。因為其使用範圍有限,這裡不再詳述。

3.使用Cython語言,一種類似於Python語言的一種新型語言編寫預定功能的代碼,然後將這些代碼轉換成為C語言編譯成為Python語言可以直接調用的二進位模組。Cython語言是融合Python語言與C語言的一種新型語言。它本身能夠理解Python語言的文法,然後在其基礎上增加了某些C語言的文法,以便更精細地控制資料類型與指標。基本相容Python文法是這個解決方案最大的特點。很多時候,Python程式員只要在舊的代碼中簡單地聲明一下代碼中所使用的參數、變數的類型,就能把立即為舊的Python程式提速。

    Cython提供了一個名為pyximporter的工具,能夠在安裝了C/C++編譯器的電腦上面為簡單的Cython程式直接產生相應的Python模組。這使得Cython的使用與普通的Python程式一樣簡單。比如下面這段代碼,直接儲存為myhello.pyx即可被調用。

  #myhello.pyx  def sayHelloTenTimes():    cdef int i #只要簡單地為變數標識類型即可加速迴圈。    for i in range(0, 10):      print("hello, world!")  $ python  >>> import pyximport; pyximport.install()  >>> import myhello  >>> myhello.sayHelloTenTimes()

    由此可見,Cython非常容易使用。而且不僅能夠處理C語言的模組,還能處理C++的模組——雖然沒有直接支援虛函數之類的完整C++特性。因為它不直接使用C/C++文法,而是另外設計比C/C++更簡潔優雅的新型文法,因此,對於不熟悉C/C++的程式員來說有很大的吸引力。相比ctypes來說,因為參數類型轉換更加智能與高效,所以通常能夠提升更多的效率。

    劣勢呢,所謂用Python程式員所熟練的文法來編寫高速的運算代碼,乍一聽相當地有吸引力。但是如果想要更深入地控制記憶體與資料結構時,程式員可能會發現,現在他不得不熟練地掌握C/C++語言,然後用Cython的文法寫出來。以程式員們懶惰的性格,這反而是件難以忍受的事件。這或許是Cython本身並不大流行的主要原因吧。

4.使用boost.python。有意思的是,與ctypes/Cython形成鮮明的對比,boost.python傾向於讓C++程式員擁有更熟悉的編程環境。它讓C++程式員使用他所熟悉的C++文法直接控制Python的資料結構,調用Python的解譯器。它沒有像Cython那樣發明新的文法,而是直接使用C++的文法,編寫供Python使用的介面。與Cython同樣的道理,它的效率優勝於ctypes。

    與Cython/SWIG/SIP等方案相比,程式員只需要學習C/C++與Python兩種語言。另外,與本文提到的幾種解決方案相比,它非常適合在主要由C++編寫的程式中控制Python代碼。不僅功能更強大、效率還更高。如此神奇的解決方案會有什麼劣勢呢?某些人可能不同意吧,老魚一聽說它依賴於boost就蔫了,感覺編譯與學習龐大又奇怪的boost非常浪費生命。

5.使用SWIG或者SIP,通過編寫一個介面檔案,使用類似於C/C++文法——聲明函數、類型的資訊,然後使用特殊的工具為C/C++的代碼產生Python的介面代碼。這些介面代碼能夠在Python與C/C++之間的資料結構轉換。最終編譯這些介面代碼,成為Python的二進位模組。SWIG與SIP的介面檔案與C/C++的標頭檔非常相似。

    這兩種工具差不多,因為。本質上,他們都與Cython類似,都使用了中繼語言來產生轉碼。但SWIG/SIP能夠在他們的介面檔案中嵌入C/C++,能夠讓程式員仔細地調節資料類型的轉換過程。在使用上,它比Cython的層次更低,更接近於Python本身提供的API。

    SWIG能夠為多種指令碼語言產生轉碼。而SIP則專門針對Python與C++。此外,SIP本身是作為PyQt的專門工具來開發的,因此它能夠理解Qt的signal/slot。從應用項目上來看,SWIG似乎會更廣泛一點。而SIP,目前所見的項目基本都與PyQt相關。據說SWIG對於C++的支援不好,不知道有沒有人來說一下呢。相比之下,SIP對於C++的支援非常完善,諸如虛函數、protected member function、模版、解構函式、異常等特性都得到良好的支援。而且SIP支援Python的GIL,還擁有一個使用Python編寫的編譯系統。可能會更方便一點。

    然而這種方案畢竟要學習一種新的語言,所以從表面上來看不如Cython和boost.python討喜。當程式員想要仔細地調節類型轉碼的時候,需要學習SWIG/SIP的內部機制,被限定使用特殊的變數名。這使得這種方案的學習曲線相對較高。

6.直接使用Python的API,可以稱之為最終解決方案。Cython, SWIG, SIP的介面檔案轉換後所產生的C/C++代碼實際上都使用Python的API。與其它方案相比,這種方案相當地繁複,必須為每次函數調用編寫資料轉碼,還要操心Python對象的引用計數。我覺得這種方案一無是處,這時就不再多講了。其它的工具pybindgen不知道什麼情況。有興趣的話可以看看。

好了。題外話一句吧,我一直覺得ctypes與xmlrpc並列Python語言的兩大神器,最能體現Python的生產效率。

希望本文在大家選擇一種技術路線時能提供一點點協助。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.