【為什麼我們要挑選這篇文章】Python 的程式碼思維很符合直觀,是個容易使用的程式語言,但卻有運算速度較慢的缺點;而 C 語言則剛好相反。

於是結合 Python 和 C 語言優點的 Cython 誕生,藉由 Cython,可以把 Python 的速度提升 36 倍。下文,是每個用 Python 的工程師都該保存下來的葵花寶典。(責任編輯:郭家宏)

Python 是工程師最喜愛的程式語言,也是目前最容易使用的語言,因為它的程式碼短小精悍,符合人們的思維方式,也符合人們的閲讀習慣。

但是你會經常聽到有人吐槽 Python,特別是有些用 C 語言的人,大力吐槽 Python 速度慢。

他們說得沒錯,相比其他高級程式設計語言,例如 C 語言,Python 確實速度很慢,這主要是因為 C 語言更面向電腦底層,像一些單晶片、電路板的設計都使用 C 語言,C 語言和組合語言之間的轉換也更快,但是 every coin has two sides,沒有完美無缺的程式語言,C 語言也有程式碼量大、偏向過程的一些缺點,如何讓 Python 兼顧 C 語言速度方面的優點呢?

舉個形象化的例子,賽車手需要兼顧汽車引擎的內耗磨損,也要兼顧賽車的運行速度,那麼,如何在兩者之間取得平衡呢?

其實有很多可以提高運行速度的辦法,例如:

利用多進程(multiprocessing)來使用所有的 CPU。
如果你正在使用 NumPy、Pandas,或是 Scikit-Learn 庫,那麼可以使用 Rapids 來提高 GPU 的處理速度。

但是這只針對你的任務可以並行的情況,例如數據預處理、矩陣操作等,上述辦法都很棒,可是如果你只使用純 Python 語言,那該怎麼辦呢?再比如,你必須使用一個很大的 for 循環,而且因為數據必須被順序處理導致你無法使用矩陣,在這種情況下,有沒有辦法提高 Python 本身的速度呢?

Cython 就是用來加速純 Python 程式碼的。

Cython:Python 和 C/C++ 的橋樑

從本質上講,Cython 是 Python 和 C/C++ 的橋樑,它允許你對 Python 程式碼稍作修改,然後把 Python 程式碼直接翻譯成 C 語言程式碼。

你唯一需要修改 Python 程式碼的地方,就是在每一個變數前面加上它的類型,通常,我們在 Python 裡會這樣聲明變數:

x = 0.5

如果使用 Cython,我們會給變數加上它的類型:

cdef float x = 0.5

這會告訴 Cython,我們的變數是浮點型變數,這和我們在 C 語言中的操作一樣。使用純 Python 語言,變數的數據類型在賦值後被自動定義。Cython 這種顯式的變數聲明方法,使得 Python 程式碼轉換成 C 程式碼成為可能,因為 C 語言要求變數的數據類型必須在聲明變數時寫出來。

使用 pip 安裝 Cython 只需一行程式碼:

pip install cython

Cython 提供的兩種數據類型:變數、函數

使用 Cython 時,Cython 提供兩類類型,一類用於變數,一類用於函數。

對於變數,我們可以這樣寫(請注意,這些類型都來自 C/C++):

cdef int a, b, c
cdef char *s
cdef float x = 0.5 (single precision)
cdef double x = 63.4 (double precision)
cdef list names
cdef dict goals_for_each_play
cdef object card_deck

對於函數,我們可以這樣寫:

def – 普通的 Python 函數,只用 Python 編譯器
cdef – Cython 專用函數,不能透過純 Python 程式碼使用該函數,必須在 Cython 內使用
cpdef – C 語言和 Python 共用,可以透過 C 語言或者 Python 程式碼使用該函數

有了對 Cython 的瞭解,我們可以更進一步,開始加速我們的程式碼了!

使用 Cython 提高程式碼的運算速度

首先,我們建立一個 Python 程式碼的基準——使用一個 for 迴圈來計算某個數的階乘。以下是用純 Python 語言寫的程式碼:

def test(x):
y = 1
for i in range(1, x+1):
y *= i
return y

使用 Cython 寫出的函數和純 Python 程式碼寫出的函數很類似,首先,我們要確保 Cython 程式碼文件的擴展名是 .pyx。然後,我們唯一修改的地方,就是在我們已聲明的每個變數和函數前加上它們的類型,run_cython.pyx 程式碼如下:

cpdef int test(int x):
cdef int y = 1
cdef int i
for i in range(1, x+1):
y *= i
return y

請注意,函數前有 cpdef,以確保我們可以透過 Python 調用該函數。同時請注意,for 迴圈裡的變數 i 也有類型資訊,你需要設定函數中所有變數的類型,這樣 C 語言編譯器才能知道需要使用什麼類型。

接下來,建立一個 setup.py 文件,把 Cython 程式碼翻譯成 C 程式碼:

from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules = cythonize(‘run_cython.pyx’))

然後執行彙編過程,在命令行輸入以下命令:

python setup.py build_ext –inplace

我們的 C 程式碼已經被編譯,可以使用了。你可以在文件夾裡看見你的 Cython 程式碼,你有了所有運行 C 程式碼所需要的文件,包括 run_cython.c 文件,文摘菌(本文作者)在 Win 10 系統嘗試後文件夾結構如下:

打開 run_cython.c 後的部分程式碼(程式碼很長,完整程式碼請參考原文出處

現在我們可以測試我們全新的、速度超快的 C 程式碼了!看看下面的程式碼,它是比較 Python 程式碼和 Cython 程式碼速度的測試:

程式碼很容易理解,我們用 Python 的方式導入我們的文件,然後也像 Python 那樣運行我們的函數。

只需稍作修改,Cython 就可以幫你加速幾乎所有的純 Python 程式碼。值得注意的是,你使用的迴圈越多、需要篩選處理的數據越多,Cython 就越能發揮加速的作用。

請看下面的表格,它記錄了 Cython 在計算不同數階乘的運行速度,number 的值從 10 到 10000000,使用 Cython,我們的速度提高了 36 倍!

以上對 Cython 的介紹,希望可以給習慣使用 Python 寫程式的讀者帶來幫助。文摘菌也大力推薦這款 Cython 加速器,讓你在 Python 的道路上馳騁!

原文報導傳送門

(本文經合作夥伴 大數據文摘 授權轉載,並同意 TechOrange 編寫導讀與修訂標題,原文標題為〈提速30倍!这个加速包让Python代码飞起来〉。首圖來源:Max Pixel CC Licensed)

更多實用的寫程式的技巧

工程師好用資源來了!超完整 Python 查詢表,程式碼複製貼上不用自己寫
【工程師精選乾貨】100 行 Python,神經網路輕鬆搞定
工程師殺手級工具!一秒自動補齊後續程式碼,還支援 23 種程式語言