Python C-API オブジェクトの割り当て



オブジェクトの作成と破棄に new 演算子と delete 演算子を使用したいと考えています。


問題は、python がいくつかの段階に分かれているように見えることです。 tp_new、tp_init、および tp_alloc は作成用であり、tp_del、tp_free、および tp_dealloc は破棄用です。ただし、c++ には、オブジェクトを割り当てて完全に構築する new と、オブジェクトを破棄して割り当てを解除する delete しかありません。


どの python tp_* メソッドを提供する必要があり、それらは何をする必要がありますか?


また、「PyObject *obj =new MyExtensionObject(args);」など、C++ でオブジェクトを直接作成できるようにしたいと考えています。これをサポートするために、何らかの方法で new 演算子をオーバーロードする必要もありますか?


また、拡張機能の型を 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();
}
...
};

答え:


これらのドキュメントは http://docs.python.org/3.0/c-api/typeobj.html にあり、
http://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__ の違いは、2 段階の初期化または 2 フェーズの初期化と呼ばれます。


あなたは「c++ には new がある」と言います " しかし、それは正しくありません。tp_alloc は C++ のカスタム アリーナ アロケータに対応し、__new__ はカスタム型アロケータ (ファクトリ関数) に対応し、__init__ はコンストラクタに似ています。最後のリンクでは、C++ と Python スタイルの類似点について詳しく説明しています。


__new__ と __init__ の相互作用の詳細については、http://www.python.org/download/releases/2.2/descrintro/ もお読みください。


「オブジェクトを直接C++で作成したい」と書いています。少なくとも、オブジェクトのインスタンス化中に発生したすべての Python 例外を C++ 例外に変換する必要があるため、これはかなり困難です。 Boost::Python を見てみると、このタスクの助けが得られるかもしれません。または、2 フェーズの初期化を使用できます。;)