扩展方法必须在非泛型静态类中定义_第11篇:Cython面向对象编程--扩展类的实例化...
我們前篇談到了Cython的訪問控制,并且談論了cdef class關鍵字的底層操作,順帶也談論了Python類為什么會比Cython類慢的原因。本篇我們將介紹Cython擴展類的初始化
Cython擴展類實例實例化,C的運行時系統在內存中都為其實例持有一個C結構體的內存區域,對于對象的創建和初始化,當Python調用__ init __時,self參數必須是該擴展類型的有效實例,當調用 __ init __時,通常使用來自參數來初始化類實例屬性,但在C底層,在調用 __ init __之前,必須為其擴展類型的實例分配內存,并且結構體的字段都必須處于有效狀態。
__ cinit __和__dealloc __方法
__ cinit__是負責執行C級別的類實例屬性(指針類型)的內存分配和初始化,并且在外部代碼的執行結束前,Cython編譯器會默認隱含調用擴展類實例的 __ dealloc __方法,這些行為和C++編譯器是一致。
- __ cinit__等同C++類中默認構造函數
- __ dealloc__ 等同于C++類的析構函數
使用__ cinit__的注意事項
- __ cinit__可能會帶來額外的開銷
- __ cinit__的參數聲明和__ init__必須一致,因為它們會被同時調用,因此__ cinit__的參數會留下kargs,*kwargs
- __ cinit__中涉及到C指針類型的類屬性的內存分配,必須在類定中顯式定義__ dealloc__,因為需要通過__ dealloc __對C指針類型的實例屬性進行內存釋放
- __ cinit__和__ init __只能使用def關鍵字申明
我們通過一些例子來列舉使用 __ cinit 的情況,下面的是一個Cython擴展類Fruit,在C級別的擴展類,若定義了C指針類型的類屬性,在 cinit__方法內完成C類型的類實例屬性的初始化和內存分配,是其主要的用途
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)調用代碼
#!/usr/bin/python3import pyximport pyximport.install()import cy_fruit if __name__=='__main__':b=cy_fruit.Fruit("banana",23.0,33.0)b.amount()Cython擴展類在構造時會保證__ cinit__只能被調用一次,并且在__ init__,__ new__或其他Python級別的構造函數(例如,類方法構造函數)之前被調用, Cython將所有初始化參數傳遞給 __ cinit __,并在./app.py的Python外部代碼在結束之前,隱式調用Fruit類實例b. __ dealloc __
Python類和Cython擴展類的實例化
我們先來比較Cython擴展類和Python類他們的初始化方式,如果你對__ new __ 和 __ init __ 存疑可以看我這篇隨筆《第6篇:Python類的__ new __和 __ init __執行原理》,再進行下面的話題
對于Cython擴展類來說,我們使用顯式調用Fruit. __ new __會比常規Fruit(...)語句執行實例化會少一個步驟,因為顯式調用Fruit. __ new __會默認只執行Fruit. __ cinit __構造函數
對于Python類來說,顯式調用Fruit. __ new __會比常規Fruit(...)語句執行實例化會少一個步驟,因為顯式調用Fruit. __ new __,會默認只執行Fruit. __ new __方法
Ok,從上面的Cython類和Python類的實例化路徑的比較,可以有一些有趣的總結
我們說對象的實例化,必須首先生成對象,然后才能構造對象,我們將Python類的 __ new __方法和Cython類的 __ cinit __方法稱為對象生成函數,并且Cython和Python允許我們將對象構造(對象的屬性初始化和對象方法的執行)的步驟,濃縮到對象生成函數中,當我們使用類似的語句“CLASS. __ new __(CLASS,....)”能快速生成并構造對象,我們將“CLASS. __ new __(CLASS,....)”這樣的語句調用稱為對象快速實例化,通過大量代碼測試,對象快速實例化和常規的對象實例化方法的性性能差距一般情況下是10-30us的差距,在Cython類中,對象快速實例化甚至會比常規的對象實例化快100us的情況都存在,如下圖
對于需要在內存中快速創建成千上萬個類對象的應用場景,以最低的時間開銷完成這些操作必須優先考慮,那么上面的對象快速實例化
Cython擴展類的快速實例化
Cython提供了兩種方法來加速擴展類型的實例化。 對于Cython擴展類型快速的實例化方案就是顯式調用__ new__方法,我們從下面的示例代碼,我們沒必要在類實現中定義__ init __方法,另外需要注意的是Cython的類定義語義中不存在 __ new __方法的定義,因為Cython類語義 __ cinit __做的事情就相當與Python類的 __ new __,像下面的代碼Fruit. __ new __(Fruit,52,32)的語句調用,實際上是調用Fruit類內部的 __ 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 對象快速實例化 - 性能比較
下面我們看看分別使用對象快速實例法和普通的對象實例化法去創建10000個Fruit對象的性能對比,首先我們都有數據源,下面是一個非常高效的read_excel函數,從excel工作簿數據加載到一個Cython類型的list當中,該函數的原本定義,請參考我之前的隨筆《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第二個性能改進適用于經常連續創建和刪除的類型,以便它們可以從空閑列表中受益。 Cython為此提供了裝飾器@ cython.freelist(N),它為給定類型創建了N個實例的靜態大小的空閑列表。 例:
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!更新中....
總結
以上是生活随笔為你收集整理的扩展方法必须在非泛型静态类中定义_第11篇:Cython面向对象编程--扩展类的实例化...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python环境管理命令_conda管理
- 下一篇: python创建一个集合_python如