Python和C/C++交互的幾種方法總結(jié)
前言
python作為一門腳本語言,其好處是語法簡單,很多東西都已經(jīng)封裝好了,直接拿過來用就行,所以實現(xiàn)同樣一個功能,用Python寫要比用C/C++代碼量會少得多。但是優(yōu)點也必然也伴隨著缺點(這是肯定的,不然還要其他語言干嘛),python最被人詬病的一個地方可能就是其運行速度了。這這是大部分腳本語言共同面對的問題,因為沒有編譯過程,直接逐行執(zhí)行,所以要慢了一大截。所以在一些對速度要求很高的場合,一般都是使用C/C++這種編譯型語言來寫。但是很多時候,我們既想使用python的簡介優(yōu)美,又不想損失太多的性能,這個時候有沒有辦法將python與C/C++結(jié)合到一起呢?這樣在性能與速度要求不高的地方,可以用pyhton寫,而關(guān)鍵的運算部分用C/C++寫,這樣就太好了。python在做科學(xué)計算或者數(shù)據(jù)分析時,這是一個非常普遍的需求。要想實現(xiàn)這個功能,python為我們提供了不止一種解決辦法。
下面我就逐一給大家介紹。
一、Cython 混合python與C
官方網(wǎng)址:http://docs.cython.org/en/latest/src/quickstart/overview.html。首先來看看cython的官方介紹吧。
[Cython] is a programming language that makes writing C extensions for the Python language as easy as Python itself. It aims to become a superset of the [Python]language which gives it high-level, object-oriented, functional, and dynamic programming. Its main feature on top of these is support for optional static type declarations as part of the language. The source code gets translated into optimized C/C++ code and compiled as Python extension modules. This allows for both very fast program execution and tight integration with external C libraries, while keeping up the high programmer productivity for which the Python language is well known.
簡單來說,cython就是一個內(nèi)置了c數(shù)據(jù)類型的python,它是一個python的超集,兼容幾乎所有的純python代碼,但是又可以使用c的數(shù)據(jù)類型。這樣就可以同時使用c庫,又不失python的優(yōu)雅。
好了,不講太多廢話,直接來看cython如何使用吧。這里的介紹大部分來自官網(wǎng),由于cython涉及到的東西還比較多,所以這里只是簡單的入門介紹,詳細(xì)的信息請移步英文官網(wǎng)。
使用cython有兩種方式:第一個是編譯生成Python擴展文件(有點類似于dll,即動態(tài)鏈接庫),可以直接import使用。第二個是使用jupyter notebook或sage notebook 內(nèi)聯(lián) cython代碼。
先看第一種。還是舉最經(jīng)典的hello world的例子吧。新建一個hello.pyx文件,定義一個hello函數(shù)如下:
def hello(name): print("Hello %s." % name)
然后,我們來寫一個setup.py 文件(寫python擴展幾乎都要寫setup.py文件,我之前也簡單介紹過怎么寫)如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2017/5/8 9:09 # @Author : Lyrichu # @Email : 919987476@qq.com # @File : setup.py ''' @Description: setup.py for hello.pyx ''' from Cython.Build import cythonize from distutils.core import setup # 編寫setup函數(shù) setup( name = "Hello", ext_modules = cythonize("hello.pyx") )
其中 ext_modules 里面寫你要 編譯的.pyx文件名字。OK,所有工作都完成了。接下來,進(jìn)入cmd,切換到setup.py 所在的文件,然后執(zhí)行命令: python setup.py build_ext --inplace
就會編譯生成一個build 文件夾以及一個.pyd文件了,這個pyd文件就是python的動態(tài)擴展庫,--inplace 的意思是在當(dāng)前文件目錄下生成.pyd文件,不加這一句就會在build文件夾中生成。
截圖如下:
圖 1
可以看出,除了生成了一個pyd文件之外,還生成了一個.c文件。test.py是我們用來測試的文件,在里面寫如下內(nèi)容:
from hello import hello hello("lyric")
從hello 模塊導(dǎo)入 hello函數(shù),然后直接調(diào)用就可以了。結(jié)果輸出 Hello lyric.
再來看如何 在 jupyter notebook中使用cython。如果你裝過ipython,一個升級版的python交互式環(huán)境,你應(yīng)該聽過 ipyhton notebook的大名,現(xiàn)在它升級了,改名叫jupyter notebook 了。簡單來說,這個就是一個可以在網(wǎng)頁環(huán)境下交互式使用python的工具,不僅可以實時看到計算結(jié)果,還可以直接展示表格,圖片等,功能還是非常強大的。首先你得安裝jupyter notebook.我印象中安裝了ipython之后應(yīng)該就會帶了jupyter了。如果沒有,可以直接 pip install jupyter
.然后輸入命令 jupyter notebook
就會在瀏覽器中打開jupyter了。
如下圖2 所示:
圖 2
點擊右上角的new按鈕,可以選擇新建一個文本文件或者文件夾,markdown或者python文件,這里我們選擇新建一個pyhton 文件,然后就會轉(zhuǎn)到一個新的窗口了,如下圖3:
圖 3
In[]:和ipython一樣,就代表著我們要輸入代碼的地方,輸入代碼之后,點擊向右的三角形符號,就會執(zhí)行代碼了。
首先輸入 %load_ext cython
,然后執(zhí)行,%開頭的語句是jupyter的魔法命令,%是行命令,%%是單元命令,具體不多說,有空給大家專門介紹一下notebook的使用。
接下來輸入:
%%cython cdef int a = 0 for i in range(10): a += i print(a)
%%cython 表明將cython內(nèi)嵌到j(luò)upyter,cdef 是cython的關(guān)鍵字,用于定義c類型,這里將a定義為c中的int類型,并且初始化為0.
然后后面的循環(huán)就是累加0到9的意思,最后輸出45.
另外,我們?nèi)绻敕治龃a 的執(zhí)行情況,可以輸入 %%cython --annotate
命令,這樣就可以輸出結(jié)果的同時,也輸出 詳細(xì)的代碼執(zhí)行情況報告了。
截圖如圖4 所示:
圖 4
jupyter notebook 可以內(nèi)嵌cython,不用我們手寫setup.py 文件,省去了編譯的過程,方便了cython的使用,所以不是正式做項目,只是寫一寫小東西用jupyter+cython還是非常方便的。
前面提到了 cdef,再舉一個稍微復(fù)雜點的例子吧。還是引用官網(wǎng)的例子,寫一個算積分的函數(shù).新建 integrate.pyx 文件,寫入如下內(nèi)容:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2017/5/8 9:26 # @Author : Lyrichu # @Email : 919987476@qq.com # @File : integrate.py ''' @Description: 積分運算,使用 cython cdef 關(guān)鍵字 ''' def f(double x): return x**2 - x def integrate_f(double a,double b,int N): cdef int i cdef double s,dx s = 0 dx = (b-a)/N for i in range(N): s += f(a + i*dx)*dx return s # 返回定積分
這段代碼應(yīng)該也是比較好理解的, f()
函數(shù)是被積函數(shù),a,b是積分的上下限,N是分割小矩形的個數(shù),注意這里將 變量i,s,dx全部都用cdef 聲明為c類型了,一般來說,在需要密集計算的地方比如循環(huán)或者復(fù)雜運算,可以將對應(yīng)的變量聲明為c類型,可以加快運行速度。
然后和上面一樣編寫 setup.py ,就是把 pyx的文件名改一下,代碼我就不貼了。然后python setup.py build_ext --inplace
執(zhí)行。得到pyd文件,編寫測試文件test.py如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2017/5/8 9:35 # @Author : Lyrichu # @Email : 919987476@qq.com # @File : test.py ''' @Description: 測試使用cython 混合c與python的integrate 函數(shù)與純python寫的integrate函數(shù)速度上的差異 ''' from integrate import integrate_f import time a = 1 # 積分區(qū)間下界 b = 2 # 積分區(qū)間上界 N = 10000 # 劃分區(qū)間個數(shù) # 使用純python代碼寫的integrate函數(shù) def py_f(x): return x**2 - x def py_integrate_f(a,b,N): dx = (b-a)/N s = 0 for i in range(N): s += py_f(a + i*dx)*dx return s start_time1 = time.time() integrate_f_res = integrate_f(a,b,N) print("integrate_f_res = %s" % integrate_f_res) end_time1 = time.time() print(u"cython 版本計算耗時:%.8f" % (end_time1 - start_time1)) start_time2 = time.time() py_integrate_f_res = py_integrate_f(a,b,N) print("py_integrate_f_res = %s" % py_integrate_f_res) end_time2 = time.time() print(u"python 版本計算耗時:%.8f" % (end_time2 - start_time2))
上面的代碼,我們重新使用python寫了一個積分函數(shù)py_integrate_f,與pyd中的integrate_f 函數(shù)進(jìn)行運算對比,結(jié)果如下(圖5):
圖5
可以看出,使用了cython的版本比純Python的版本大概快了4、5倍的樣子,而這僅僅是將幾個變量改為c類型的結(jié)果,可見,cython確實可以方便地對python與c進(jìn)行混合,獲得速度上的提升,又不失去Python的簡潔優(yōu)美。
最后再來說下cython 如何調(diào)用c libraries. C 語言 stdlib 庫有一個 atoi函數(shù),可以將字符串轉(zhuǎn)化為整數(shù),math庫有一個sin函數(shù),我們就以這兩個函數(shù)為例。新建 calling_c.pyx 文件,文件內(nèi)容如下:
from libc.stdlib cimport atoi from libc.math cimport sin def parse_char_to_int(char * s): assert s is not NULL,"byte string value is NULL" return atoi(s) def f_sin_squared(double x): return sin(x*x)
前兩行導(dǎo)入了C語言中的函數(shù),然后我們自定義了兩個函數(shù),parse_char_to_int 可以將字符串轉(zhuǎn)換為整數(shù),f_sin_squared 計算 x平方的sin函數(shù)值。寫 setup.py 文件,和之前差不多,但是要注意的是,在unix系統(tǒng)下,math庫默認(rèn)是不鏈接的,所以需要指明其位置,那么在unix系統(tǒng)下,setup.py 文件的內(nèi)容就需要增加Extension 一項,如下:
from distutils.core import setup from distutils.extension import Extension from Cython.Build import cythonize ext_modules=[ Extension("calling_c", sources=["calling_c.pyx"], libraries=["m"] # Unix-like specific ) ] setup( name = "Calling_c", ext_modules = cythonize(ext_modules) )
然后直接編即可。test.py文件如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2017/5/8 12:21 # @Author : Lyrichu # @Email : 919987476@qq.com # @File : test.py ''' @Description: test file ''' from calling_c import f_sin_squared,parse_char_to_int str = "012" str_b = bytes(str,encoding='utf-8') n = parse_char_to_int(str_b) print("n = %d" %n) from math import pi,sqrt x = sqrt(pi/2) res = f_sin_squared(x) print("sin(pi/2)=%f" % res)
需要注意的是,Python字符串不能直接傳入 parse_char_to_int
函數(shù),需要將其轉(zhuǎn)換為 bytes 類型再傳入。運行結(jié)果為:
n = 12 sin(pi/2)=1.000000
如果不想通過libc導(dǎo)入c語言模塊,cython也允許我們自己聲明c函數(shù)原型來導(dǎo)入,一個例子如下:
# 自己聲明c函數(shù)原型 cdef extern from "math.h": cpdef double cos(double x) def f_cos(double x): return cos(x)
使用了 extern 關(guān)鍵字。
每次都編寫setup.py 文件,然后編譯,略顯麻煩。cython還提供了一種更簡單的方法:pyximport。通過導(dǎo)入pyximport(安裝cython時會自動安裝),在沒有引入額外的c庫的情況下,可以直接調(diào)用pyx中的函數(shù),更為直接與方便。以前面的hello 模塊為例,編寫好hello.py文件之后,編寫一個pyximport_test.py 文件,文件內(nèi)容如下:
import pyximport pyximport.install() import hello hello.hello("lyric")
直接運行就會發(fā)現(xiàn),確實可以正確導(dǎo)入hello模塊。
cython的更多內(nèi)容,請大家自行訪問官網(wǎng)查看。
其他python與c/c++ 混合編程的方式主要還有 使用 ctypes,cffi模塊以及swig。本來想一起寫的,想想還是分開寫吧,不然太長了。后續(xù)會陸續(xù)更新,敬請關(guān)注。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
VSCode配置python環(huán)境及中文問題解決方法
這篇文章主要介紹了VSCode配置python環(huán)境及中文問題,print打印中文亂碼如何解決這個問題呢,本文給大家?guī)韮煞N方法幫助大家解決這個問題,需要的朋友可以參考下2022-02-02Python編程實現(xiàn)刪除VC臨時文件及Debug目錄的方法
這篇文章主要介紹了Python編程實現(xiàn)刪除VC臨時文件及Debug目錄的方法,涉及Python針對文件與目錄的遍歷、刪除等相關(guān)操作技巧,需要的朋友可以參考下2017-03-03Python如何使用argparse模塊處理命令行參數(shù)
這篇文章主要介紹了Python如何使用argparse模塊處理命令行參數(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12基于matplotlib+tkinter實現(xiàn)簡單的繪圖系統(tǒng)
在理解matplotlib嵌入到tkinter中的原理之后,就已經(jīng)具備了打造繪圖系統(tǒng)的技術(shù)基礎(chǔ),所以本文來實現(xiàn)一個簡單的繪圖系統(tǒng),感興趣的小伙伴小伙伴可以了解一下2023-08-08python GUI庫圖形界面開發(fā)之PyQt5不規(guī)則窗口實現(xiàn)與顯示GIF動畫的詳細(xì)方法與實例
這篇文章主要介紹了python GUI庫圖形界面開發(fā)之PyQt5不規(guī)則窗口與顯示GIF動畫的詳細(xì)方法與實例,需要的朋友可以參考下2020-03-03