python做爲一門腳本語言,其好處是語法簡單,不少東西都已經封裝好了,直接拿過來用就行,因此實現一樣一個功能,用Python寫要比用C/C++代碼量會少得多。可是優勢也必然也伴隨着缺點(這是確定的,否則還要其餘語言幹嗎),python最被人詬病的一個地方可能就是其運行速度了。這這是大部分腳本語言共同面對的問題,由於沒有編譯過程,直接逐行執行,因此要慢了一大截。因此在一些對速度要求很高的場合,通常都是使用C/C++這種編譯型語言來寫。可是不少時候,咱們既想使用python的簡介優美,又不想損失太多的性能,這個時候有沒有辦法將python與C/C++結合到一塊兒呢?這樣在性能與速度要求不高的地方,能夠用pyhton寫,而關鍵的運算部分用C/C++寫,這樣就太好了。python在作科學計算或者數據分析時,這是一個很是廣泛的需求。要想實現這個功能,python爲咱們提供了不止一種解決辦法。下面我就逐一給你們介紹。html
1、Cython 混合python與Cpython
官方網址:http://docs.cython.org/en/latest/src/quickstart/overview.html。首先來看看cython的官方介紹吧。c++
[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就是一個內置了c數據類型的python,它是一個python的超集,兼容幾乎全部的純python代碼,可是又可使用c的數據類型。這樣就能夠同時使用c庫,又不失python的優雅。瀏覽器
好了,不講太多廢話,直接來看cython如何使用吧。這裏的介紹大部分來自官網,因爲cython涉及到的東西還比較多,因此這裏只是簡單的入門介紹,詳細的信息請移步英文官網。markdown
使用cython有兩種方式:第一個是編譯生成Python擴展文件(有點相似於dll,即動態連接庫),能夠直接import使用。第二個是使用jupyter notebook或sage notebook 內聯 cython代碼。函數
先看第一種。仍是舉最經典的hello world的例子吧。新建一個hello.pyx文件,定義一個hello函數以下:工具
def hello(name): print("Hello %s." % name)
而後,咱們來寫一個setup.py 文件(寫python擴展幾乎都要寫setup.py文件,我以前也簡單介紹過怎麼寫)以下:性能
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # @Time : 2017/5/8 9:09 4 # @Author : Lyrichu 5 # @Email : 919987476@qq.com 6 # @File : setup.py 7 ''' 8 @Description: setup.py for hello.pyx 9 ''' 10 from Cython.Build import cythonize 11 from distutils.core import setup 12 13 # 編寫setup函數 14 setup( 15 name = "Hello", 16 ext_modules = cythonize("hello.pyx") 17 )
其中 ext_modules 裏面寫你要 編譯的.pyx文件名字。OK,全部工做都完成了。接下來,進入cmd,切換到setup.py 所在的文件,而後執行命令: python setup.py build_ext --inplace 就會編譯生成一個build 文件夾以及一個.pyd文件了,這個pyd文件就是python的動態擴展庫,--inplace 的意思是在當前文件目錄下生成.pyd文件,不加這一句就會在build文件夾中生成。截圖以下:測試
圖 1
能夠看出,除了生成了一個pyd文件以外,還生成了一個.c文件。test.py是咱們用來測試的文件,在裏面寫以下內容:
from hello import hello hello("lyric")
從hello 模塊導入 hello函數,而後直接調用就能夠了。結果輸出 Hello lyric.
再來看如何 在 jupyter notebook中使用cython。若是你裝過ipython,一個升級版的python交互式環境,你應該聽過 ipyhton notebook的大名,如今它升級了,更名叫jupyter notebook 了。簡單來講,這個就是一個能夠在網頁環境下交互式使用python的工具,不只能夠實時看到計算結果,還能夠直接展現表格,圖片等,功能仍是很是強大的。首先你得安裝jupyter notebook.我印象中安裝了ipython以後應該就會帶了jupyter了。若是沒有,能夠直接 pip install jupyter .而後輸入命令 jupyter notebook 就會在瀏覽器中打開jupyter了。以下圖2 所示:
圖 2
點擊右上角的new按鈕,能夠選擇新建一個文本文件或者文件夾,markdown或者python文件,這裏咱們選擇新建一個pyhton 文件,而後就會轉到一個新的窗口了,以下圖3:
圖 3
In[]:和ipython同樣,就表明着咱們要輸入代碼的地方,輸入代碼以後,點擊向右的三角形符號,就會執行代碼了。
首先輸入 %load_ext cython ,而後執行,%開頭的語句是jupyter的魔法命令,%是行命令,%%是單元命令,具體很少說,有空給你們專門介紹一下notebook的使用。
接下來輸入:
1 %%cython 2 cdef int a = 0 3 for i in range(10): 4 a += i 5 print(a)
%%cython 代表將cython內嵌到jupyter,cdef 是cython的關鍵字,用於定義c類型,這裏將a定義爲c中的int類型,而且初始化爲0.
而後後面的循環就是累加0到9的意思,最後輸出45.
另外,咱們若是想分析代碼 的執行狀況,能夠輸入 %%cython --annotate 命令,這樣就能夠輸出結果的同時,也輸出 詳細的代碼執行狀況報告了。截圖如圖4 所示:
圖 4
jupyter notebook 能夠內嵌cython,不用咱們手寫setup.py 文件,省去了編譯的過程,方便了cython的使用,因此不是正式作項目,只是寫一寫小東西用jupyter+cython仍是很是方便的。
前面提到了 cdef,再舉一個稍微複雜點的例子吧。仍是引用官網的例子,寫一個算積分的函數.新建 integrate.pyx 文件,寫入以下內容:
#!/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 關鍵字 ''' 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 # 返回定積分
這段代碼應該也是比較好理解的,f()函數是被積函數,a,b是積分的上下限,N是分割小矩形的個數,注意這裏將 變量i,s,dx所有都用cdef 聲明爲c類型了,通常來講,在須要密集計算的地方好比循環或者複雜運算,能夠將對應的變量聲明爲c類型,能夠加快運行速度。
而後和上面同樣編寫 setup.py ,就是把 pyx的文件名改一下,代碼我就不貼了。而後python setup.py build_ext --inplace 執行。獲得pyd文件,編寫測試文件test.py以下:
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # @Time : 2017/5/8 9:35 4 # @Author : Lyrichu 5 # @Email : 919987476@qq.com 6 # @File : test.py 7 ''' 8 @Description: 測試使用cython 混合c與python的integrate 函數與純python寫的integrate函數速度上的差別 9 ''' 10 from integrate import integrate_f 11 import time 12 13 a = 1 # 積分區間下界 14 b = 2 # 積分區間上界 15 N = 10000 # 劃分區間個數 16 17 # 使用純python代碼寫的integrate函數 18 def py_f(x): 19 return x**2 - x 20 21 def py_integrate_f(a,b,N): 22 dx = (b-a)/N 23 s = 0 24 for i in range(N): 25 s += py_f(a + i*dx)*dx 26 return s 27 28 start_time1 = time.time() 29 integrate_f_res = integrate_f(a,b,N) 30 print("integrate_f_res = %s" % integrate_f_res) 31 end_time1 = time.time() 32 print(u"cython 版本計算耗時:%.8f" % (end_time1 - start_time1)) 33 34 start_time2 = time.time() 35 py_integrate_f_res = py_integrate_f(a,b,N) 36 print("py_integrate_f_res = %s" % py_integrate_f_res) 37 end_time2 = time.time() 38 print(u"python 版本計算耗時:%.8f" % (end_time2 - start_time2))
上面的代碼,咱們從新使用python寫了一個積分函數py_integrate_f,與pyd中的integrate_f 函數進行運算對比,結果以下(圖5):
圖5
能夠看出,使用了cython的版本比純Python的版本大概快了四、5倍的樣子,而這僅僅是將幾個變量改成c類型的結果,可見,cython確實能夠方便地對python與c進行混合,得到速度上的提高,又不失去Python的簡潔優美。
最後再來講下cython 如何調用c libraries. C 語言 stdlib 庫有一個 atoi函數,能夠將字符串轉化爲整數,math庫有一個sin函數,咱們就以這兩個函數爲例。新建 calling_c.pyx 文件,文件內容以下:
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)
前兩行導入了C語言中的函數,而後咱們自定義了兩個函數,parse_char_to_int 能夠將字符串轉換爲整數,f_sin_squared 計算 x平方的sin函數值。寫 setup.py 文件,和以前差很少,可是要注意的是,在unix系統下,math庫默認是不連接的,因此須要指明其位置,那麼在unix系統下,setup.py 文件的內容就須要增長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文件以下:
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # @Time : 2017/5/8 12:21 4 # @Author : Lyrichu 5 # @Email : 919987476@qq.com 6 # @File : test.py 7 ''' 8 @Description: test file 9 ''' 10 from calling_c import f_sin_squared,parse_char_to_int 11 str = "012" 12 str_b = bytes(str,encoding='utf-8') 13 n = parse_char_to_int(str_b) 14 print("n = %d" %n) 15 from math import pi,sqrt 16 x = sqrt(pi/2) 17 res = f_sin_squared(x) 18 print("sin(pi/2)=%f" % res)
須要注意的是,Python字符串不能直接傳入 parse_char_to_int 函數,須要將其轉換爲 bytes 類型再傳入。運行結果爲:
n = 12
sin(pi/2)=1.000000
若是不想經過libc導入c語言模塊,cython也容許咱們本身聲明c函數原型來導入,一個例子以下:
# 本身聲明c函數原型 cdef extern from "math.h": cpdef double cos(double x) def f_cos(double x): return cos(x)
使用了 extern 關鍵字。
每次都編寫setup.py 文件,而後編譯,略顯麻煩。cython還提供了一種更簡單的方法:pyximport。經過導入pyximport(安裝cython時會自動安裝),在沒有引入額外的c庫的狀況下,能夠直接調用pyx中的函數,更爲直接與方便。之前面的hello 模塊爲例,編寫好hello.py文件以後,編寫一個pyximport_test.py 文件,文件內容以下:
import pyximport pyximport.install() import hello hello.hello("lyric")
直接運行就會發現,確實能夠正確導入hello模塊。
cython的更多內容,請你們自行訪問官網查看。
其餘python與c/c++ 混合編程的方式主要還有 使用 ctypes,cffi模塊以及swig。原本想一塊兒寫的,想一想仍是分開寫吧,否則太長了。後續會陸續更新,敬請關注。