在Python中,一切都是對象。 具體來說是什么意思? 在最基本的層面上,一個對象具有三樣東西
- 標識(id):對象的標識將其與其他對象區(qū)分開來,并由id內(nèi)置函數(shù)提供
- 屬性值(value):對象的值就是與之關聯(lián)的數(shù)據(jù),可以通過點表示法進行訪問。通常,Python將對象的數(shù)據(jù)放在名為__ dict __的內(nèi)部實例字典中。
- 類型(type):對象指定該類型的對象表現(xiàn)出的行為。 這些行為可以通過稱為方法的特殊功能進行訪問。 當在對象上調(diào)用方法時,類型負責創(chuàng)建和銷毀其對象,對其進行初始化以及更新其值。Python允許我們使用class語句在Python代碼中創(chuàng)建新類型。
Python類型系統(tǒng)

我們知道Python的面向對象編程中,允許用戶自定義的數(shù)據(jù)類型,在實例化Python的類對象時,該實例的屬性值會保存到其內(nèi)部dict中,即__ dict __屬性,具有原生Python語義的類,其對象實例化前由Python解析器執(zhí)行并經(jīng)歷如下過程(有些Python讀物將這個過程步驟2到步驟4叫動態(tài)分派(Dynamic Dispatch),這里僅給出總結性的描述)

(首次加載序列化mashling格式的字節(jié)碼)→從字節(jié)碼緩存文件中解析→實例屬性的類型動態(tài)檢查→確定實例屬性類型(類屬性的尺寸)→為其在實例化分配堆內(nèi)存空間
- 內(nèi)置類型(Builtin Type):像object,list,dict,file,int float的數(shù)據(jù)類型,我們稱為Python的內(nèi)置數(shù)據(jù)類型,他們是通過Python的底層C接口實現(xiàn)的,并且默認整合到Python的運行時環(huán)境。
- 用戶定義類型(User Defined Type):在Python中通過使用關鍵字class定義的用戶自定義數(shù)據(jù)類型。需要強調(diào)的該類型是具備Python語義的類(也就是純Python代碼定義的類),因此需要更多的開銷。
- 擴展數(shù)據(jù)類型(Extend Type):通過Pytho底層C接口定義的數(shù)據(jù)類型,例如C的關鍵字定義的struct,C++的關鍵字定義的class/struct定義的類,Cython的cdef class關鍵字定義的類.
不同類型的組合關系:
- 在原生的Python代碼中,不同Builtin Type(或擴展類型)以任意數(shù)量且不同類型的組合出包羅萬象的User Definded Type,
- 在原生的Python代碼中 任意不同的User Defined Type的組合可以組合出規(guī)模更大的User Defined Type,組合的規(guī)模越大內(nèi)存開銷和實例化的時間開銷就越大,并且在所有情況組合中,純Python定義/組合的User Defined Type是最慢的。
- 由于Builtin Type和Extend Type在C/C++底層定義并且在編譯時類型靜態(tài)綁定,因此初始化時繞過Python解析器的動態(tài)分派過程,直接提供高效的C級別內(nèi)存分配和內(nèi)存訪問,因此以C/C++級別中,以任意不同的Builtin-Type或Extend Type組合出規(guī)模更大的Extend Type,性能相比User Defined Type仍然高出許多。
要直接在C/C++中實現(xiàn)Python的擴展類型需要開發(fā)者熟悉Python C底層接口,因此并不適合沒有C/C++經(jīng)驗的開發(fā)者,然而Cython已經(jīng)高度集成幾乎所有C/C++的主流特性。能夠以類似Python的語法快速定義出C/C++級別的Python擴展類型。
Cython中的擴展類
下面是一個簡單的水果類,每種水果都有名稱,數(shù)量,價格,F(xiàn)ruit類在純Python的解析級別上定義(默認在.py文件中),當然也可以由Cython編譯為C擴展
class Fruit(object):
'''Fruit Type'''
def __init__(self,nm,qt,pc):
self.name=nm
self.qty=qt
self.price=pc
def amount(self):
return self.qty*self.price
在純Python代碼定義中,我們Frui對象實例化的時間開銷是265ns

在Cython編譯后的Fruit2對象(類定義是一樣,類名稱不一樣),時間開銷是217ns

當我們使用cython將Fruit類編譯為C時,生成的類只是Python在C級別的用戶自定義類型,而不是擴展類型。 當Cython將其編譯為C時,它仍然可以使用所有操作的動態(tài)分派的通用Python對象來實現(xiàn)。 所生成的代碼大量使用Python底層C應用接口,并且與使用純Python代碼定義此類時的Python解釋器進行相同的調(diào)用。
- 由于消除了解釋器解讀字節(jié)碼的開銷,因此Cython版本的Fruit類僅具有很小的性能提升。
- 它不能從任何靜態(tài)類型中受益,因為Fruit類的C實現(xiàn)仍然必須依靠動態(tài)分派來在運行時解析類型。
ss8.png
要將上面的Python類轉化為C級別的擴展類,只需對Fruit類做以下代碼修改
- 使用cdef關鍵字修飾class關鍵字,就是告知Cython編譯器,將類編譯成C級別的擴展類型
- 在類級別下,使用cdef關鍵字聲明類屬性,并且為類屬性靜態(tài)指定C數(shù)據(jù)類型,目地類實例化時是繞過Python解釋其
%%cython -a
cdef class Fruit(object):
'''Fruit Type'''
#類屬性靜態(tài)
cdef str name
cdef double qty
cdef double price
def __init__(self,nm,qt,pc):
self.name=nm
self.qty=qt
self.price=pc
def amount(self):
return self.qty*self.price
通過對Cython版本的Fruit類的類屬性聲明語句進行類型靜態(tài)化后,我們實例化該實例的時間開銷非常棒,最好的成績是85.5ns

Cython編譯的擴展類型比純Python定義的用戶定義類型快3倍多,比Cython編譯的用戶定義類型快2倍

目前這個Cython版本的Fruit類設計還不是最優(yōu)的版本,我們下篇繼續(xù)探討有關Cython面向對象編程中的更多內(nèi)容。
更新中....

