扩展方法必须在非泛型静态类中定义_第11篇:Cython面向对象编程--扩展类的实例化...
我們前篇談到了Cython的訪問控制,并且談?wù)摿薱def class關(guān)鍵字的底層操作,順帶也談?wù)摿薖ython類為什么會比Cython類慢的原因。本篇我們將介紹Cython擴展類的初始化
Cython擴展類實例實例化,C的運行時系統(tǒng)在內(nèi)存中都為其實例持有一個C結(jié)構(gòu)體的內(nèi)存區(qū)域,對于對象的創(chuàng)建和初始化,當(dāng)Python調(diào)用__ init __時,self參數(shù)必須是該擴展類型的有效實例,當(dāng)調(diào)用 __ init __時,通常使用來自參數(shù)來初始化類實例屬性,但在C底層,在調(diào)用 __ init __之前,必須為其擴展類型的實例分配內(nèi)存,并且結(jié)構(gòu)體的字段都必須處于有效狀態(tài)。
__ cinit __和__dealloc __方法
__ cinit__是負(fù)責(zé)執(zhí)行C級別的類實例屬性(指針類型)的內(nèi)存分配和初始化,并且在外部代碼的執(zhí)行結(jié)束前,Cython編譯器會默認(rèn)隱含調(diào)用擴展類實例的 __ dealloc __方法,這些行為和C++編譯器是一致。
- __ cinit__等同C++類中默認(rèn)構(gòu)造函數(shù)
- __ dealloc__ 等同于C++類的析構(gòu)函數(shù)
使用__ cinit__的注意事項
- __ cinit__可能會帶來額外的開銷
- __ cinit__的參數(shù)聲明和__ init__必須一致,因為它們會被同時調(diào)用,因此__ cinit__的參數(shù)會留下kargs,*kwargs
- __ cinit__中涉及到C指針類型的類屬性的內(nèi)存分配,必須在類定中顯式定義__ dealloc__,因為需要通過__ dealloc __對C指針類型的實例屬性進行內(nèi)存釋放
- __ cinit__和__ init __只能使用def關(guān)鍵字申明
我們通過一些例子來列舉使用 __ cinit 的情況,下面的是一個Cython擴展類Fruit,在C級別的擴展類,若定義了C指針類型的類屬性,在 cinit__方法內(nèi)完成C類型的類實例屬性的初始化和內(nèi)存分配,是其主要的用途
from libc.stdlib cimport malloc,freecdef class Fruit(object):'''Fruit Type'''cdef readonly str namecdef public double qtycdef readonly double pricecdef double *weightdef __cinit__(self,nm,qt,pc):print("__cinit__ method executed")self.name=nmself.qty=qtself.price=pcself.weight=<double*>malloc(sizeof(double))self.weight[0]=23.33print("weight: ",self.weight[0])def __init__(self,nm,qt,pc):print("__init__ method executed")def amount(self):return self.qty*self.pricedef __dealloc__(self):print("__dealloc__method executed")if self.weight!=NULL:free(self.weight)調(diào)用代碼
#!/usr/bin/python3import pyximport pyximport.install()import cy_fruit if __name__=='__main__':b=cy_fruit.Fruit("banana",23.0,33.0)b.amount()Cython擴展類在構(gòu)造時會保證__ cinit__只能被調(diào)用一次,并且在__ init__,__ new__或其他Python級別的構(gòu)造函數(shù)(例如,類方法構(gòu)造函數(shù))之前被調(diào)用, Cython將所有初始化參數(shù)傳遞給 __ cinit __,并在./app.py的Python外部代碼在結(jié)束之前,隱式調(diào)用Fruit類實例b. __ dealloc __
Python類和Cython擴展類的實例化
我們先來比較Cython擴展類和Python類他們的初始化方式,如果你對__ new __ 和 __ init __ 存疑可以看我這篇隨筆《第6篇:Python類的__ new __和 __ init __執(zhí)行原理》,再進行下面的話題
對于Cython擴展類來說,我們使用顯式調(diào)用Fruit. __ new __會比常規(guī)Fruit(...)語句執(zhí)行實例化會少一個步驟,因為顯式調(diào)用Fruit. __ new __會默認(rèn)只執(zhí)行Fruit. __ cinit __構(gòu)造函數(shù)
對于Python類來說,顯式調(diào)用Fruit. __ new __會比常規(guī)Fruit(...)語句執(zhí)行實例化會少一個步驟,因為顯式調(diào)用Fruit. __ new __,會默認(rèn)只執(zhí)行Fruit. __ new __方法
Ok,從上面的Cython類和Python類的實例化路徑的比較,可以有一些有趣的總結(jié)
我們說對象的實例化,必須首先生成對象,然后才能構(gòu)造對象,我們將Python類的 __ new __方法和Cython類的 __ cinit __方法稱為對象生成函數(shù),并且Cython和Python允許我們將對象構(gòu)造(對象的屬性初始化和對象方法的執(zhí)行)的步驟,濃縮到對象生成函數(shù)中,當(dāng)我們使用類似的語句“CLASS. __ new __(CLASS,....)”能快速生成并構(gòu)造對象,我們將“CLASS. __ new __(CLASS,....)”這樣的語句調(diào)用稱為對象快速實例化,通過大量代碼測試,對象快速實例化和常規(guī)的對象實例化方法的性性能差距一般情況下是10-30us的差距,在Cython類中,對象快速實例化甚至?xí)瘸R?guī)的對象實例化快100us的情況都存在,如下圖
對于需要在內(nèi)存中快速創(chuàng)建成千上萬個類對象的應(yīng)用場景,以最低的時間開銷完成這些操作必須優(yōu)先考慮,那么上面的對象快速實例化
Cython擴展類的快速實例化
Cython提供了兩種方法來加速擴展類型的實例化。 對于Cython擴展類型快速的實例化方案就是顯式調(diào)用__ new__方法,我們從下面的示例代碼,我們沒必要在類實現(xiàn)中定義__ init __方法,另外需要注意的是Cython的類定義語義中不存在 __ new __方法的定義,因為Cython類語義 __ cinit __做的事情就相當(dāng)與Python類的 __ new __,像下面的代碼Fruit. __ new __(Fruit,52,32)的語句調(diào)用,實際上是調(diào)用Fruit類內(nèi)部的 __ cinit __方法
#cython:language_level=3cdef class Fruit(object):'''Fruit Type'''cdef readonly str namecdef public double qtycdef readonly double pricedef __cinit__(self,str nm,double qt,double pc):self.name=nmself.qty=qtself.price=pcdef amount(self):return self.qty*self.pricedef __repr__(self):return "name:{},qty:{},price:{}".format(self.name,self.qty,self.price) #end-class常歸對象實例法 vs 對象快速實例化 - 性能比較
下面我們看看分別使用對象快速實例法和普通的對象實例化法去創(chuàng)建10000個Fruit對象的性能對比,首先我們都有數(shù)據(jù)源,下面是一個非常高效的read_excel函數(shù),從excel工作簿數(shù)據(jù)加載到一個Cython類型的list當(dāng)中,該函數(shù)的原本定義,請參考我之前的隨筆《Python的openpyxl性能加速》
%%cython from openpyxl import load_workbookcpdef list read_excel(str filename):wb=load_workbook(filename,read_only=True)ws=wb['sheet1']cdef list data=list(),neRowcdef int rdx,cdxfor rdx,row in enumerate(ws.rows):neRow=[]for cdx,cell in enumerate(row):neRow.append(cell.value)if len(neRow):data.append(neRow)return data快速實例化的代碼主體
from cy_fruit import Fruitcdef list data=read_excel("./Vegetable_Fruits.xlsx") cdef list item,fruitListfor item in data:fruit.append(Fruit.__new__(Fruit,item[1],item[2],item[3])del item第二個性能改進適用于經(jīng)常連續(xù)創(chuàng)建和刪除的類型,以便它們可以從空閑列表中受益。 Cython為此提供了裝飾器@ cython.freelist(N),它為給定類型創(chuàng)建了N個實例的靜態(tài)大小的空閑列表。 例:
cimport cython@cython.freelist(8) cdef class Penguin:cdef object fooddef __cinit__(self, food):self.food = foodpenguin = Penguin('fish 1') penguin = None penguin = Penguin('fish 2') # does not need to allocate memory!更新中....
總結(jié)
以上是生活随笔為你收集整理的扩展方法必须在非泛型静态类中定义_第11篇:Cython面向对象编程--扩展类的实例化...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python环境管理命令_conda管理
- 下一篇: 产线数字化软件源码_数字化工厂规划的十大