使用 C 優化你的 Python 代碼
Cython 是 Python 編程語言的編譯器,旨在優化性能並形成一個擴展的 Cython 編程語言。作為 Python 的擴展,Cython 也是 Python 語言的超集,它支持調用 C 函數和在變數和類屬性上聲明 C 類型。這使得包裝外部 C 庫、將 C 嵌入現有應用程序或者為 Python 編寫像 Python 一樣簡單的 C 語言擴展語法變得容易。
Cython 一般用於創建 C 模塊來加速 Python 代碼的執行。這在使用解釋型語言編寫的效率不高的複雜應用中非常重要。
安裝 Cython
你可以在 Linux、BSD、Windows 或 macOS 上安裝 Cython 來使用 Python:
$ python -m pip install Cython
安裝好後,就可以使用它了。
將 Python 轉換成 C
使用 Cython 的一個好的方式是從一個簡單的 「hello world」 開始。這雖然不是展示 Cython 優點的最好方式,但是它展示了使用 Cython 時發生的情況。
首先,創建一個簡單的 Python 腳本,文件命名為 hello.pyx
(.pyx
擴展名並不神奇,從技術上它可以是任何東西,但它是 Cython 的默認擴展名):
print("hello world")
接下來,創建一個 Python 設置腳本。一個像 Python 的 makefile 一樣的 setup.py
,Cython 可以使用它來處理你的 Python 代碼:
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("hello.pyx")
)
最後,使用 Cython 將你的 Python 腳本轉換為 C 代碼:
$ python setup.py build_ext --inplace
你可以在你的工程目錄中看到結果。Cython 的 cythonize
模塊將 hello.pyx
轉換成一個 hello.c
文件和一個 .so
庫。這些 C 代碼有 2648 行,所以它比一個一行的 hello.pyx
源碼的文本要多很多。.so
庫也比它的源碼大 2000 倍(即 54000 位元組和 20 位元組相比)。然後,Python 需要運行單個 Python 腳本,所以有很多代碼支持這個只有一行的 hello.pyx
文件。
要使用 Python 的 「hello world」 腳本的 C 代碼版本,請打開一個 Python 提示符並導入你創建的新 hello
模塊:
>>> import hello
hello world
將 C 代碼集成到 Python 中
測試計算能力的一個很好的通用測試是計算質數。質數是一個比 1 大的正數,且它只有被 1 或它自己除後才會產生正整數。雖然理論很簡單,但是隨著數的變大,計算需求也會增加。在純 Python 中,可以用 10 行以內的代碼完成質數的計算。
import sys
number = int(sys.argv[1])
if not number <= 1:
for i in range(2, number):
if (number % i) == 0:
print("Not prime")
break
else:
print("Integer must be greater than 1")
這個腳本在成功的時候是不會提醒的,如果這個數不是質數,則返回一條信息:
$ ./prime.py 3
$ ./prime.py 4
Not prime.
將這些轉換為 Cython 需要一些工作,一部分是為了使代碼適合用作庫,另一部分是為了提高性能。
腳本和庫
許多用戶將 Python 當作一種腳本語言來學習:你告訴 Python 想讓它執行的步驟,然後它來做。隨著你對 Python(以及一般的開源編程)的了解越多,你可以了解到許多強大的代碼都存在於其他應用程序可以利用的庫中。你的代碼越 不具有針對性,程序員(包括你)就越可能將其重用於其他的應用程序。將計算和工作流解耦可能需要更多的工作,但最終這通常是值得的。
在這個簡單的質數計算的例子中,將其轉換成 Cython,首先是一個設置腳本:
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("prime.py")
)
將你的腳本轉換成 C:
$ python setup.py build_ext --inplace
到目前為止,一切似乎都工作的很好,但是當你試圖導入並使用新模塊時,你會看到一個錯誤:
>>> import prime
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "prime.py", line 2, in init prime
number = sys.argv[1]
IndexError: list index out of range
這個問題是 Python 腳本希望從一個終端運行,其中參數(在這個例子中是要測試是否為質數的整數)是一樣的。你需要修改你的腳本,使它可以作為一個庫來使用。
寫一個庫
庫不使用系統參數,而是接受其他代碼的參數。對於用戶輸入,與其使用 sys.argv
,不如將你的代碼封裝成一個函數來接收一個叫 number
(或者 num
,或者任何你喜歡的變數名)的參數:
def calculate(number):
if not number <= 1:
for i in range(2, number):
if (number % i) == 0:
print("Not prime")
break
else:
print("Integer must be greater than 1")
這確實使你的腳本有些難以測試,因為當你在 Python 中運行代碼時,calculate
函數永遠不會被執行。但是,Python 編程人員已經為這個問題設計了一個通用、還算直觀的解決方案。當 Python 解釋器執行一個 Python 腳本時,有一個叫 __name__
的特殊變數,這個變數被設置為 __main__
,但是當它被作為模塊導入的時候,__name__
被設置為模塊的名字。利用這點,你可以寫一個既是 Python 模塊又是有效 Python 腳本的庫:
import sys
def calculate(number):
if not number <= 1:
for i in range(2, number):
if (number % i) == 0:
print("Not prime")
break
else:
print("Integer must be greater than 1")
if __name__ == "__main__":
number = sys.argv[1]
calculate( int(number) )
現在你可以用一個命令來運行代碼了:
$ python ./prime.py 4
Not a prime
你可以將它轉換為 Cython 來用作一個模塊:
>>> import prime
>>> prime.calculate(4)
Not prime
C Python
用 Cython 將純 Python 的代碼轉換為 C 代碼是有用的。這篇文章描述了如何做,然而,Cython 還有功能可以幫助你在轉換之前優化你的代碼,分析你的代碼來找到 Cython 什麼時候與 C 進行交互,以及更多。如果你正在用 Python,但是你希望用 C 代碼改進你的代碼,或者進一步理解庫是如何提供比腳本更好的擴展性的,或者你只是好奇 Python 和 C 是如何協作的,那麼就開始使用 Cython 吧。
via: https://opensource.com/article/21/4/cython
作者:Alan Smithee 選題:lujun9972 譯者:ShuyRoy 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive