<cython>學習筆記第二章:編譯和運行cython

Cython第二章,編譯和運行cython


編譯Pipeline

由于cython是python的超集,因此python解釋器不能直接解釋cython的代碼,通過cython compilation pipeline,可以將cython代碼轉換為python的擴展模塊。pipeline分為兩階段:

  1. cython代碼轉化為c或c++
  2. 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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容