第6篇:Cython的面向對象--Python類 vs Cython擴展類

在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)容。

更新中....

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

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容