2009-02-21 93 views
5

我想使用new和delete操作符來創建和銷燬我的對象。Python C-API對象分配

問題是python似乎把它分成幾個階段。 tp_new,tp_init和tp_alloc用於創建,tp_del,tp_free和tp_dealloc用於銷燬。然而,C++只是有了新的分配和完全構造對象並刪除了哪些對象進行了析構和釋放。

我需要提供哪些python tp_ *方法?他們必須執行哪些操作?另外我想能夠直接在C++中創建對象,例如「PyObject * obj = new MyExtensionObject(args);」我是否還需要以某種方式重載新操作員以支持此操作?

我也希望能夠在python中繼承我的擴展類型,有什麼特別的我需要做以支持這個嗎?

我正在使用python 3.0.1。編輯: 好吧,tp_init似乎讓我的對象有點太可變(例如,創建一個Texture對象,在創建完成後更改內容很好,但改變它的基本方面,如大小, bitdept等會破壞很多現有的C++東西,假設這些東西是固定的)。如果我沒有實現它,它會阻止人們在其構造後調用__init__(或者至少忽略這個調用,就像元組一樣)。或者,如果tp_init在同一個對象上被多次調用,我是否應該有一些拋出異常或異常的標誌?

除此之外,我認爲我的大部分其餘的排序。

extern "C" 
{ 
    //creation + destruction 
    PyObject* global_alloc(PyTypeObject *type, Py_ssize_t items) 
    { 
     return (PyObject*)new char[type->tp_basicsize + items*type->tp_itemsize]; 
    } 
    void global_free(void *mem) 
    { 
     delete[] (char*)mem; 
    } 
} 
template<class T> class ExtensionType 
{ 
    PyTypeObject *t; 
    ExtensionType() 
    { 
     t = new PyTypeObject();//not sure on this one, what is the "correct" way to create an empty type object 
     memset((void*)t, 0, sizeof(PyTypeObject)); 
     static PyVarObject init = {PyObject_HEAD_INIT, 0}; 
     *((PyObject*)t) = init; 

     t->tp_basicsize = sizeof(T); 
     t->tp_itemsize = 0; 

     t->tp_name = "unknown"; 

     t->tp_alloc = (allocfunc) global_alloc; 
     t->tp_free = (freefunc) global_free; 
     t->tp_new  = (newfunc) T::obj_new; 
     t->tp_dealloc = (destructor)T::obj_dealloc; 
     ... 
    } 
    ...bunch of methods for changing stuff... 
    PyObject *Finalise() 
    { 
    ... 
    } 
}; 
template <class T> PyObjectExtension : public PyObject 
{ 
... 
    extern "C" static PyObject* obj_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) 
    { 
     void *mem = (void*)subtype->tp_alloc(subtype, 0); 
     return (PyObject*)new(mem) T(args, kwds) 
    } 
    extern "C" static void obj_dealloc(PyObject *obj) 
    { 
     ~T(); 
     obj->ob_type->tp_free(obj);//most of the time this is global_free(obj) 
    } 
... 
}; 
class MyObject : PyObjectExtension<MyObject> 
{ 
public: 
    static PyObject* InitType() 
    { 
     ExtensionType<MyObject> extType(); 
     ...sets other stuff... 
     return extType.Finalise(); 
    } 
    ... 
}; 

回答

11

這些文檔是在http://docs.python.org/3.0/c-api/typeobj.htmlhttp://docs.python.org/3.0/extending/newtypes.html介紹如何使自己的類型。

tp_alloc執行實例的低級內存分配。這相當於malloc(),並且將refcnt初始化爲1. Python擁有自己的分配器PyType_GenericAlloc,但是一個類型可以實現專門的分配器。

tp_new與Python的__new__相同。它通常用於數據存儲在實例本身中的不可變對象,與指向數據的指針相比較。例如,字符串和元組將它們的數據存儲在實例中,而不是使用char *或PyTuple *。

對於這種情況,tp_new根據輸入參數計算出需要多少內存,並調用tp_alloc來獲取內存,然後初始化基本字段。 tp_new不需要調用tp_alloc。它可以例如返回一個緩存的對象。

tp_init與Python的__init__相同。大部分的初始化應該在這個函數中。

__new__和__init__之間的區別稱爲two-stage initializationtwo-phase initialization

你說「C++只是有新的」但這是不正確的。 tp_alloc對應於C++中的自定義競技場分配器,__new__對應於自定義類型分配器(工廠函數),__init__更像構造器。最後一個鏈接討論更多關於C++和Python風格之間的相似之處。

另請閱讀http://www.python.org/download/releases/2.2/descrintro/瞭解有關__new__和__init__如何交互的詳細信息。

你寫道你想「直接用C++創建對象」。這很困難,因爲至少你必須將在對象實例化期間發生的任何Python異常轉換爲C++異常。您可以嘗試查看Boost :: Python以獲取此任務的一些幫助。或者你可以使用兩階段初始化。 ;}

0

我根本不知道python API,但是如果python拆分了分配和初始化,你應該可以使用placement new。

例如爲:

// tp_alloc 
void *buffer = new char[sizeof(MyExtensionObject)]; 
// tp_init or tp_new (not sure what the distinction is there) 
new (buffer) MyExtensionObject(args); 
return static_cast<MyExtensionObject*>(buffer); 

... 
// tp_del 
myExtensionObject->~MyExtensionObject(); // call dtor 
// tp_dealloc (or tp_free? again I don't know the python apis) 
delete [] (static_cast<char*>(static_cast<void*>(myExtensionObject))); 
+1

Placement`new`會直觀,但可悲的是`new(p)Class(args);`在調用構造函數之前執行零初始化操作,並且清除python分配/初始化代碼放入對象的值內存(例如引用計數)。另外,使用`new char [n]`覆蓋結構是很危險的,因爲保證的內存對齊方式只有1。 – 2015-03-16 04:55:39