Cython第二章,編譯和運行cython
編譯Pipeline
由于cython是python的超集,因此python解釋器不能直接解釋cython的代碼,通過cython compilation pipeline,可以將cython代碼轉換為python的擴展模塊。pipeline分為兩階段:
- cython代碼轉化為c或c++
- c/c++代碼編譯成.so共享庫
標準編譯路徑
python的標準庫distutils package用來編譯、打包、發(fā)布python項目,其中一個功能是將C代碼編譯成python的擴展模塊。也就是編譯pipeline中的第二個階段。第一個階段則交給cythonize命令。
一個純cython代碼可以使用簡單的setup.py腳本編譯成為.so動態(tài)庫,考慮以下cython代碼(位置:/examples/02-compiling/distutils/fib.pyx):
def fib(long n):
'''Returns the nth Fibonacci number.'''
cdef long a=0, b=1, i
for i in range(n):
a, b = a + b, a
return a
由于是純cython代碼,可以使用以下setup.py進行編譯
from distutils.core import setup
from Cython.Build import cythonize
#cythonize:編譯源代碼為C或C++,返回一個distutils Extension對象列表
setup(ext_modules=cythonize('fib.pyx'))
執(zhí)行
python setup.py build_ext -i
在同一目錄下,得到fib.c build/ 和擴展庫fib.so,fib.so是可以通過from fib import fib進行調用的。
使用cython包裝純c代碼的編譯
在使用cython包裝c代碼時,編譯時,需要指定額外的原文件,例如:
cfib.c
#include "cfib.h"
unsigned long long fib(unsigned long n) {
unsigned long a=0, b=1, i, tmp;
for (i=0; i<n; ++i) {
tmp = a; a = a + b; b = tmp;
}
return a;
}
wrap_fib.pyx
cdef extern from "cfib.h":
unsigned long _fib "fib"(unsigned long n)
def fib(n):
''' Returns the nth Fibonacci number.'''
return _fib(n)
cfib.h略,在這種情況下,使用需要傳遞給cythonize的內容不能是wrap_fib.pyx,而是將wrap_fib.pyx與源文件cfib.c打包成一個Extension對象,setup.py如下:
from distutils.core import setup, Extension
from Cython.Build import cythonize
# First create an Extension object with the appropriate name and sources
ext = Extension(name="wrap_fib", sources=["cfib.c", "wrap_fib.pyx"])
# Use cythonize on the extension object.
setup(ext_modules=cythonize(ext))
執(zhí)行
python setup.py build_ext -i
得到wrap_fib.so
如果cfib.c已經是預編譯成動態(tài)庫libfib.so,那么setup.py例子如下:
from distutils.core import setup, Extension
from Cython.Build import cythonize
# First create an Extension object with the appropriate name and sources
ext = Extension(name="wrap_fib",
sources=["wrap_fib.pyx"],
library_dirs=["/path/libfib.so"],
libraries=["fib"])
# Use cythonize on the extension object.
setup(ext_modules=cythonize(ext))
IPython與Cython的動態(tài)編譯(%%cython magic)
Ipython的一個擴展功能是能夠動態(tài)的編譯cython代碼為python擴展模塊,打開這個功能,執(zhí)行命令%load_ext cythonmagic,然后使用%%cython編寫動態(tài)的cython代碼,例子如下:
%%cython
def fib(int n):
cdef int i
cdef double a=0.0,b=1.0
for i in range(n):
a,b=a+b,a
return a
ipython將會把以上的代碼在后臺進行編譯成.so然后導入當前的解釋器,我們可以直接調用fib函數。
%%cython 命令可以接受一些參數,--cplus c++代碼,-I include,-c 編譯參數。
pyximport,import .pyx文件時進行編譯
pyximport可以在import一個pyx文件時動態(tài)對其進行編譯,由于這種方法依賴于cython和gcc,因此不適合應用在生產系統(tǒng)上,例子:
import pyximport
#在import之前調用install命令
pyximport.install()
#import fib.pyx,和import fib.py的感覺一樣
import fib
pyimport還能夠處理更加復雜的情況,例如,可以處理cython文件中需要引用的別的c、c++頭文件,還可以處理cython包含的其他cython文件。我們引入一個.pyxdeps文件來記錄這些外部包含關系,將包含所有依賴文件的list,一行一個。pyxdeps文件只能處理代碼的依賴性,如何將幾個源代碼編譯和連接成一個python擴展模塊,這還需要一個.pyxbld文件,.pyxbld文件中將包含一個或兩個函數:
make_ext(modname,pyxfilename)
接受兩個參數,返回一個Extension對象
make_setup_args
如果定義了,pyximport將額外的參數傳遞給setup函數
例子(位置/examples/02-compiling/pyximport):
_fic.c略
_fib.h略
fib.pyx
#cfib函數包裝了_fib.c中的_cfib函數
cdef extern from "_fib.h":
unsigned long int _cfib "fib"(unsigned long int)
def cfib(unsigned long int n):
return _cfib(n)
#使用cython寫的fib函數
def fib(long n):
'''Returns the nth Fibonacci number.'''
cdef long a=0, b=1, i
for i in range(n):
a, b = a + b, a
return a
fib.pyxbld
def make_ext(modname, pyxfilename):
from distutils.extension import Extension
return Extension(modname,
#告訴distutils把哪些源文件link起來
sources=[pyxfilename, '_fib.c'],
#告訴distutils把在哪個路徑查找頭文件
include_dirs = ['.'])
# def make_setup_args():
# return dict(script_args=["--compiler=mingw32"])
執(zhí)行pyximport就可以導入fib模塊中的cfib和fib函數了
手工編譯cython
這一節(jié)討論如何手工編譯cython代碼,就以上一節(jié)的_fib.c,_fib.h,fib.pyx為例,fib.pyx中本身包含一個fib函數,同時包含一個包裝_fib.c的函數cfib, 例子:
#fib.pyx->fib.c
cython fib.pyx
#CLFAGS\LDFLAGS,編譯連接參數
CFLAGS=$(python-config --cflags)
LDFLAGS=$(python-config --ldflags)
#分別編譯fib.c和_fib.c為fib.o和_fib.o
gcc -c fib.c ${CFLAGS}
gcc -c _fib.c ${CFLAGS}
#連接
gcc fib.o _fib.o -o fib.so -shared ${LDFLAGS}
得到可調用的fib.so