作者:
日期:
目錄
1 簡(jiǎn) 介一種為Python寫C擴(kuò)展的方式,嘗試一下。 參考文獻(xiàn):
2 基 本使用Cython基于pyrex,但是擁有更多功能和優(yōu)化。用來(lái)寫Python的C擴(kuò)展的,并生成有效的C代碼。寫出的文件擴(kuò)展名是 ".pyx" ,已經(jīng)可以算作一種語(yǔ)言了。 一個(gè)簡(jiǎn)單的加法函數(shù)( addtest.pyx ): def addtest(a,b): cdef float c=a+b return c 編譯和生成動(dòng)態(tài)庫(kù): cython addtest.pyx gcc -c -fPIC -I/usr/include/python2.5 addtest.c gcc -shared addtest.o -o addtest.so 使用: $ python >>> import addtest >>> addtest(1,2) 3.0 構(gòu)建Cython代碼的方式:
使用 setup.py 方式,例如一個(gè) hello.pyx 文件,編寫的 setup.py 如下: from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext ext_modules=[Extension('hello',['hello.pyx'])] setup( name='Hello world app', cmdclass={'build_ext':build_ext}, ext_modules=ext_modules ) 構(gòu)建使用命令 python setup.py build_ext --inplace 。 Cython提高速度的主要原因是使用靜態(tài)類型??梢栽谌魏螀?shù)前直接使用C的類型定義。函數(shù)內(nèi)的話要加"cdef"前綴。如: def f(double x): cdef double ret ret=x**2-x return ret 僅僅使用Cython編譯純Python代碼可以提高35%的性能,幾乎全部使用靜態(tài)類型以后提高4倍。 C風(fēng)格函數(shù)聲明,"except? -2"表示返回-2時(shí)就是出錯(cuò)了。不過(guò)"except *"是肯定安全的。如: cdef double f(double) except? -2: return x**2-x 使用cpdef時(shí),這個(gè)函數(shù)就可以同時(shí)被C和Python調(diào)用了。當(dāng)使用了C函數(shù)時(shí),因?yàn)楸荛_(kāi)了昂貴的函數(shù)調(diào)用,旺旺可以提高150倍的速度。 不要過(guò)度優(yōu)化,一步步的優(yōu)化并且查看profile。使用"cython -a"參數(shù)可以查看HTML報(bào)告。 3 調(diào) 用其他C庫(kù)3.1 簡(jiǎn) 單例子導(dǎo)入"math.h"中的 sin() 函數(shù)并使用: cdef extern from "math.h": double sin(double) cdef double f(double x): return sin(x*x) Cython不會(huì)去掃描頭文件,所以自己必須再聲明一遍。下面是使用時(shí)必須連接上其他庫(kù)的 setup.py 文件: from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext ext_modules=[ Extension('demo',['demo.pyx',],libraries=['m',]) ] setup( name='Demos', cmdclass={'build_ext':build_ext}, ext_modules=ext_modules, ) 同理可以使用任何動(dòng)態(tài)或靜態(tài)編譯的庫(kù)。 3.2 重 新定義外部C庫(kù)的定義一段C代碼,頭文件中的類型定義與函數(shù)聲明: typedef struct _Queue Queue; typedef void *QueueValue; Queue *queue_new(void); void queue_free(Queue *queue); int queue_push_head(Queue *queue, QueueValue data); QueueValue queue_pop_head(Queue *queue); QueueValue queue_peek_head(Queue *queue); int queue_push_tail(Queue *queue, QueueValue data); QueueValue queue_pop_tail(Queue *queue); QueueValue queue_peek_tail(Queue *queue); int queue_is_empty(Queue *queue); 對(duì)應(yīng)的Cython定義,寫入一個(gè)".pxd"文件中: cdef extern from "libcalg/queue.h": ctypedef struct Queue: pass ctypedef void* QueueValue Queue* new_queue() void queue_free(Queue* queue) int queue_push_head(Queue* queue, QueueValue data) QueueValue queue_pop_head(Queue* queue) QueueValue queue_peek_head(Queue* queue) int queue_push_tail(Queue* queue, QueueValue data) QueueValue queue_pop_tail(Queue* queue) QueueValue queue_peek_tail(Queue* queue) bint queue_is_empty(Queue* queue) 大部分時(shí)候這種聲明與頭文件幾乎是一樣的,你可以直接拷貝過(guò)來(lái)。唯一的區(qū)別在最后一行,C函數(shù)的返回值其實(shí)是布爾值,所以用bint類型會(huì)轉(zhuǎn)換成 Python的布爾值。 這里可以不關(guān)心結(jié)構(gòu)體的內(nèi)容,而只是用它的名字。 4 類 定義一個(gè)類的例子: cimport cqueue cimport python_exc cdef class Queue: cdef cqueue.Queue_c_queue def __cinit__(self): self._c_queue=cqueue.new_queue() 這里的構(gòu)造函數(shù)是 __cinit__() 而不是 __init__() 。雖然 __init__() 依然有效,但是并不確保一定會(huì)運(yùn)行(比如子類忘了調(diào)用基類的構(gòu)造函數(shù))。因?yàn)槲闯跏蓟闹羔樈?jīng)常導(dǎo)致Python掛掉而沒(méi)有任何提示,所以 __cinit__() 總是會(huì)在初始化時(shí)調(diào)用。不過(guò)其被調(diào)用時(shí),對(duì)象尚未構(gòu)造完成,所以除了cdef字段以外,避免其他操作。如果要給 __cinit__() 構(gòu)造和函數(shù)加參數(shù),必須與 __init__() 的匹配。 構(gòu)造函數(shù)初始化資源時(shí)記得看看返回的資源是否是有效的。如果無(wú)效可以拋出錯(cuò)誤。Cython提供了內(nèi)存不足異常,如下: def __cinit__(self): self._c_queue=cqueue.new_queue() if self._c_queue is NULL: python_exc.PyErr_NoMemory() Cython提供的析構(gòu)函數(shù),僅在建立成功內(nèi)部對(duì)象時(shí)釋放內(nèi)部對(duì)象: def __dealloc__(self): if self._c_queue is not NULL: cqueue.queue_free(self._c_queue) 將數(shù)據(jù)以通用指針?lè)绞竭M(jìn)入,和返回時(shí)的強(qiáng)制類型轉(zhuǎn)換: cdef append(self,int value): cqueue.queue_push_tail(self._c_queue,<void*>value) cdef int pop(self): return <int>cqueue.queue_pop_head(self._c_queue) Cython除了支持普通Python類以外,還支持?jǐn)U展類型,使用"cdef class"定義。在內(nèi)存占用和效率上更好。因?yàn)槭褂肅結(jié)構(gòu)體存儲(chǔ)字段和方法,而不是Python字典。所以可以存儲(chǔ)任意C字段類型,而不是其 Python包裝。訪問(wèn)時(shí)也是直接訪問(wèn)C的值,而不是通過(guò)字典查找。 普通的Python類可以繼承自cdef類,但是反之則不行。Cython需要知道完整的繼承層次來(lái)定義C結(jié)構(gòu)體,并且嚴(yán)格限制單繼承。不過(guò)普通 Python類可以繼承任一數(shù)量的Python類和擴(kuò)展類型,無(wú)論在Python中還是在Cython代碼中。 5 與 Python交互如果Cython調(diào)用Python函數(shù)失敗,則直接返回NULL,而不是異常對(duì)象。 如果一個(gè)函數(shù)既有可能返回NULL,也有可能返回0,則處理起來(lái)就比較麻煩。Python C API的做法是 PyErr_Occurred() 函數(shù)。不過(guò)這種方式有性能損耗。在Cython中你可以自己指定哪個(gè)返回值代表錯(cuò)誤,所以環(huán)境只要檢查這個(gè)返回值即可。其他所有值都回?zé)o損耗的被接受。 在函數(shù)定義時(shí)指定except子句,則僅在函數(shù)返回該值時(shí)檢查是否需要拋出異常。這樣同一個(gè)函數(shù)返回0和返回0同時(shí)返回錯(cuò)誤就可以區(qū)分開(kāi)。例子: cdef int pop(self) except? 0: #... 類中的 cdef 定義C方法,而 cpdef 可以同時(shí)定義C方法和Python方法。 |
|