Python C-APIを使用して(こことここのように)クラスプロパティを作成するための最良の方法は何ですか?私の場合、静的プロパティも機能します。
ファローアップ:
ヤクの提案を実装してみました。とスロットP
にget関数とset関数を含むクラスを定義しました。次に、キーの下のクラスのタイプオブジェクトのディクショナリにのインスタンスを追加しました。それでtp_descr_get
tp_descr_set
P
X
p
x1 = X()
x2 = X()
x1.p = 10
print x1.p
print x2.p
print X.p
x2.p = 11
print x1.p
print x2.p
print X.p
動作します(最初の10は3回印刷され、次に11は3回印刷されます)が、
X.p = 12
エラーメッセージで失敗する
TypeError: can't set attributes of built-in/extension type 'X'
どうすれば修正できますか?
フォローアップ2:
タイプオブジェクトを割り当ててフラグPyMem_Malloc
を設定すると、すべてが機能します。Py_TPFLAGS_HEAPTYPE
私はX.p = 12
期待された結果で行うことができます。
型オブジェクトを静的変数に保持してPy_TPFLAGS_HEAPTYPE
フラグを設定しても問題はありませんが、それは明らかに良い考えではありません。(しかし、タイプオブジェクトが静的メモリにあるか動的メモリにあるかが重要なのはなぜですか?とにかくその参照カウントを0に落とすことはありません。)
動的型にのみ属性を設定できるという制限は非常に奇妙に思えます。この背後にある理論的根拠は何ですか?
フォローアップ3:
いいえ、機能しません。タイプをX
動的にすると、プロパティは12にX.p = 12
設定されません。X.p
実際には、オブジェクト12
を名前にバインドしますX.p
。つまり、その後X.p
は整数値のプロパティではなく、整数になります。
フォローアップ4:
拡張機能のC++コードは次のとおりです。
#include <python.h>
#include <exception>
class ErrorAlreadySet : public std::exception {};
// P type ------------------------------------------------------------------
struct P : PyObject
{
PyObject* value;
};
PyObject* P_get(P* self, PyObject* /*obj*/, PyObject* /*type*/)
{
Py_XINCREF(self->value);
return self->value;
}
int P_set(P* self, PyObject* /*obj*/, PyObject* value)
{
Py_XDECREF(self->value);
self->value = value;
Py_XINCREF(self->value);
return 0;
}
struct P_Type : PyTypeObject
{
P_Type()
{
memset(this, 0, sizeof(*this));
ob_refcnt = 1;
tp_name = "P";
tp_basicsize = sizeof(P);
tp_descr_get = (descrgetfunc)P_get;
tp_descr_set = (descrsetfunc)P_set;
tp_flags = Py_TPFLAGS_DEFAULT;
if(PyType_Ready(this)) throw ErrorAlreadySet();
}
};
PyTypeObject* P_type()
{
static P_Type typeObj;
return &typeObj;
}
// P singleton instance ----------------------------------------------------
P* createP()
{
P* p_ = PyObject_New(P, P_type());
p_->value = Py_None;
Py_INCREF(p_->value);
return p_;
}
P* p()
{
static P* p_ = createP();
Py_INCREF(p_);
return p_;
}
PyObject* p_value()
{
PyObject* p_ = p();
PyObject* value = p()->value;
Py_DECREF(p_);
Py_INCREF(value);
return value;
}
// X type ------------------------------------------------------------------
struct X : PyObject {};
void X_dealloc(PyObject* self)
{
self->ob_type->tp_free(self);
}
struct X_Type : PyTypeObject
{
X_Type()
{
memset(this, 0, sizeof(*this));
ob_refcnt = 1;
tp_name = "M.X";
tp_basicsize = sizeof(X);
tp_dealloc = (destructor)X_dealloc;
tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
tp_dict = PyDict_New();
PyObject* key = PyString_FromString("p");
PyObject* value = p();
PyDict_SetItem(tp_dict, key, value);
Py_DECREF(key);
Py_DECREF(value);
if(PyType_Ready(this)) throw ErrorAlreadySet();
}
void* operator new(size_t n) { return PyMem_Malloc(n); }
void operator delete(void* p) { PyMem_Free(p); }
};
PyTypeObject* X_type()
{
static PyTypeObject* typeObj = new X_Type;
return typeObj;
}
// module M ----------------------------------------------------------------
PyMethodDef methods[] =
{
{"p_value", (PyCFunction)p_value, METH_NOARGS, 0},
{0, 0, 0, 0}
};
PyMODINIT_FUNC
initM(void)
{
try {
PyObject* m = Py_InitModule3("M", methods, 0);
if(!m) return;
PyModule_AddObject(m, "X", (PyObject*)X_type());
}
catch(const ErrorAlreadySet&) {}
}
このコードは、前に説明したように、クラスプロパティをM
持つクラスを持つモジュールを定義します。また、プロパティを実装するオブジェクトを直接検査できる関数を追加しました。X
p
p_value()
拡張機能をテストするために使用したスクリプトは次のとおりです。
from M import X, p_value
x1 = X()
x2 = X()
x1.p = 1
print x1.p
print x2.p
print X.p
print p_value()
print
x2.p = 2
print x1.p
print x2.p
print X.p
print p_value()
print
X.p = 3
print x1.p
print x2.p
print X.p
print p_value() # prints 2
print
x1.p = 4 # AttributeError: 'M.X' object attribute 'p' is read-only