Python C ++ API - как получить доступ / установить атрибуты класса из Python? - PullRequest
0 голосов
/ 10 мая 2019

Я создал getsetters для публичной переменной number_bananas В моем классе Box . number_bananas является общедоступным, потому что коробка разблокирована, любой может съесть бананы или положить больше в коробку.

Вот мой тип PyBox:

typedef struct 
{
    PyObject_HEAD
    Box *bx;
} PyBox;

В моей коробкекласс, который я определил:

class Box {
   public:
      Box(double l, double b, double h);
      int number_bananas;
   ...

Box::Box(double l, double b, double h)
{
        number_bananas = 11;
        length         = l;
        breadth        = b;
        height         = h;       
}

и вот определенные мной методы get / setter:

static PyObject *pyBox_getBananas(PyBox *self, void *closure)
{
    Py_INCREF(self->bx->number_bananas);
    return self->bx->number_bananas;
}

static int pyBox_setBananas(PyBox *self, PyObject *value, void *closure)
{
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "You're trying to put something that is not a banana!!");
        return -1;
    }

    Py_DECREF(self->bx->number_bananas);
    Py_INCREF(value);
    self->bx->number_bananas = value;

    return 0;
}

static PyGetSetDef pyBox_getseters[] = {
    {"number_bananas", (getter)pyBox_getBananas, (setter)pyBox_setBananas, "number of bananas", NULL},
    {NULL}
};

Конструктор, который я использовал для создания экземпляра класса Box, определяется следующим образом:

static int pyBox_init(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char* nams[] = {"length","breadth","height", NULL};
    int l, b, h;
    if(!PyArg_ParseTupleAndKeywords(args, kwds, "iii", nams, &l, &b, &h))
        return -1;

    ((PyBox *)self)->bx = &(Box::Box(l,b,h));

    return 0;
}

static PyObject *pyBox_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PyBox *self;
    self = (PyBox *) type->tp_alloc(type, 0);

    return (PyObject *)self;
}

Модуль успешно компилируется.Однако я не могу найти, как получить доступ к свойству number_bananas из Python.

Следующие результаты приводят к фатальной ошибке (segfault):

import adventureIsland

bo = adventureIsland.box(1,1,1)

print(bo.number_bananas)

Итак ... мойВопрос в том, как мне получить доступ / установить number_bananas из Python?

Спасибо!

1 Ответ

1 голос
/ 11 мая 2019

self->bx->number_bananas не является PyObject*, поэтому не может быть увеличено или уменьшено. Вместо этого вы хотите конвертировать его в / из PyObject*. В Python3 вы делаете это с различными PyLong_* функциями .

Некоторым непроверенным кодом будет:

static PyObject *pyBox_getBananas(PyBox *self, void *closure)
{
    return PyLong_FromLong(self->bx->number_bananas);
}

static int pyBox_setBananas(PyBox *self, PyObject *value, void *closure)
{
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "You're trying delete the attribute!!");
        return -1;
    }

    int valuei = PyLong_AsLong(value);
    if (valuei==-1 and PyErr_Occurrred()) {
        return -1;
    }

    self->bx->number_bananas = valuei;

    return 0;
}

Потенциально могут возникнуть проблемы со слишком большими числами, если int (number_of_bananas) и long (результат PyLong_AsLong) не имеют одинаковый размер, который, возможно, стоит попытаться определить и поднять исключение.


У вас были дополнительные проблемы из-за проблемы с конструктором:

((PyBox *)self)->bx = &(Box::Box(l,b,h));

Устанавливает bx для указания временного Box, который перестает существовать почти сразу после его создания. Что вы должны сделать, это выделить Box в куче с помощью оператора new. (Что происходит сейчас, так это то, что память, на которую вы указываете, повторно используется в других функциях, поэтому «значение» меняется путаницей).

((PyBox *)self)->bx = new Box::Box(l,b,h);

Затем вы должны убедиться, что вы delete указали это в деструкторе (чтобы избежать утечки памяти).

...