Свойство класса с использованием Python C -API

Как лучше всего создать свойства класса(как здесь и здесь ), используя API Python C -? Статические свойства также будут работать в моем случае.

Дополнение:

Я попытался реализовать предложение яка. Я определил класс Pс функциями get и set в слотах tp_descr_getи tp_descr_set. Затем я добавил экземпляр Pв словарь типа object для класса 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 печатается три раза, затем 11 печатается три раза ), но

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динамическим, то X.p = 12не присвоит свойству X.pзначение двенадцать; он фактически связывает объект 12с именем X.p. Другими словами, впоследствии X.pявляется не целочисленным -оцениваемым свойством, а целым числом.

Дополнение 4:

Вот код C++ для расширения:

#include 
#include 

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

7
задан Community 23 May 2017 в 11:58
поделиться