8

Python C-APIを使用して(ここここのように)クラスプロパティを作成するための最良の方法は何ですか?私の場合、静的プロパティも機能します。

ファローアップ:

ヤクの提案を実装してみました。とスロットPにget関数とset関数を含むクラスを定義しました。次に、キーの下のクラスのタイプオブジェクトのディクショナリにのインスタンスを追加しました。それでtp_descr_gettp_descr_setPXp

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持つクラスを持つモジュールを定義します。また、プロパティを実装するオブジェクトを直接検査できる関数を追加しました。Xpp_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
4

4 に答える 4

5

これらの Python ソリューションと同様にclassproperty、C で型を作成し、そのtp_descr_get関数を実装する必要があります ( __get__Python での対応)。

次に、それを C 型で使用する場合は、型のインスタンスを作成し、それを型の辞書 (型のスロット) にclassproperty挿入する必要があります。tp_dict

ファローアップ:

C 型の属性を設定することは不可能のようです。tp_setattroメタクラス ( )の関数は、すべての非ヒープ型 (フラグPyType_Typeのない型) に対して「組み込み/拡張型の属性を設定できません」という例外を発生させます。Py_TPFLAGS_HEAPTYPEこのフラグは、動的タイプに対して設定されます。型を動的にすることもできますが、その価値よりも多くの作業が必要になる場合があります。

これは、私が最初に示した解決策では、読み取り専用という制限付きで、C 型オブジェクトにプロパティ (計算された属性など) を作成できることを意味します。設定には、class/static-method ( のメソッドのMETH_CLASS/METH_STATICフラグtp_methods) を使用できます。

于 2012-04-16T00:06:03.107 に答える
4

クラスの静的プロパティの使用について発見したことの本質を伝えようとします。

私の(編集された)コードは次のとおりです。

// Prepare your type object, which you will no doubt complete 
// by calling PyType_Ready, as here.
if (PyType_Ready(typeObj) < 0)
{
  return;
}

Py_INCREF(typeObj);
PyModule_AddObject(module, typeName, (PyObject*) typeObj);

// Now we add static members directly to the type's tp_dict, but 
// only *after* we've registered the type (above)
PyObject* dict = typeObj->tp_dict;

// You'll have your own wrapper / C values in the line below. This is just
// a pseudo-version of what I am (successfully) using.
PyObject* tmp = MyCreateWrapper(myStaticValueInC);

// Py_INCREF(tmp); // You may need this, depending on how line above works.

PyDict_SetItemString(dict, staticPropertyName, tmp);

Py_DECREF(tmp);

クラス プロパティを実装するためにコードを構築する順序に関して、すべての必須事項はここにあると思います。

于 2017-09-14T12:30:30.207 に答える
1

それが許容できる解決策である場合は、 の宣言を保持するモジュールにメソッドを作成して、X必要に応じてクラス変数を設定するだけです。例えば:

PyObject* set_p_value(PyObject*, PyObject* o) {
  if(PyDict_SetItemString(X_type()->tp_dict, "p", o) == -1) return 0;
  Py_RETURN_NONE; 
}

PyMethodDef methods[] = 
{
    ...
    {"set_p_value", (PyCFunction)set_p_value, METH_O, 0},
    {0, 0, 0, 0}
};

それができたら、以下より:

from M import X, set_p_value
set_p_value(3)
print X.p #should print '3'

期待どおりに動作するはずです。欠点は、残念ながら、この機能が型オブジェクト自体とは無関係であることです。必要に応じてクラス変数を設定するクラス メソッドを指定すると、これを部分的に回避できます。

于 2014-04-23T16:37:48.230 に答える