Создание массива numpy объектов пользовательского класса с помощью C API

Используя C API, я хотел бы создать массив numpy, содержащий объекты типа Quaternion, который является классом, который я написал на C++. У меня уже есть массив из них (на самом деле std::vector), и я хочу сделать копию или использовать ту же память, если это возможно.

Поскольку это не базовый тип, мне нужно использовать типы Py_Object, и я не могу использовать PyArray_SimpleNew или что-то в этом роде.

Я предполагаю, что, возможно, захочу использовать PyArray_NewFromDescr или даже PyArray_SimpleNewFromDescr, но я полностью и совершенно не понимаю, как мне создать объект PyArray_Descr, который мне нужен для описания моего класса Quaternion.

Может ли кто-нибудь дать мне несколько советов о том, как сделать этот объект descr? Или дайте мне лучшее представление о том, как построить мой массив numpy?

По сути, это более общая версия этого вопроса., не отвлекаясь.

РЕДАКТИРОВАТЬ:

Используя подсказку dastrobu и свою обертку SWIG, я нашел способ сделать это. Я знаю, что не все используют SWIG, но для тех, кто использует мой ответ на мой другой вопрос, показано, как я это сделал вне.


person Mike    schedule 23.10.2013    source источник


Ответы (1)


Поскольку Quaternion не является напрямую числовым типом, ваш массив должен иметь numpy.object как dtype. Следовательно, вы можете использовать PyArray_SimpleNew(..., NPY_OBJECT) для создания массива и заполнения данных. Проблема в том, что ваш класс Quaternion не является типом Python. Так что заполнение массива ссылками на объекты типа Quaternion не получится. (В этом случае, что вы ожидаете, если вы извлечете элемент из массива, заполненного кватернионами из python?) Вместо этого вам нужно обернуть класс Quaternion чем-то вроде PyQuaternion. Оболочка заботится о подсчете ссылок и управлении памятью. Это будет выглядеть примерно так:

typedef struct {
    PyObject_HEAD
    Quaternion *q;
}PyQuaternion;

static PyTypeObject PyQuaternion_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                                        /*ob_size*/
    "Quaternion",                             /*tp_name*/
    sizeof(PyQuaternion),                     /*tp_basicsize*/
/* ... */
};


static PyObject *
PyQuaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds){
/* ... */
};

static int 
PyQuaternion_init(PyQuaternion *self, PyObject *args, PyObject *kwds){
/* ... */
};

static void PyQuaternion_dealloc(PyQuaternion *self){
/* ... */
};

Кроме того, вы можете определить свой собственный C-API для PyQuaternionType, что позволит вам создать PyQuaternions из Quaternions.

static PyObject *
PyQuaternion_New(Quaternion *q){
    PyQuaternion *self;
    self = (PyQuaternion *)PyQuaternion_Type.tp_new(type, NULL, NULL);
    self->q = q; 
    return (PyObject *)self;
}

Имейте в виду, что self->q будет обрабатываться функцией PyQuaternion_dealloc, поэтому подумайте об управлении памятью. Самым простым было бы передать право собственности оболочке и позволить PyQuaternion_dealloc освободить self->q.

Функция PyQuaternion_New позволяет вам оборачивать объекты Quaternion и заполнять их любым контейнером Python, например, списками, кортежами и, конечно же, массивами numpy с dtype = numpy.object.

person dastrobu    schedule 23.10.2013
comment
Спасибо. Это имеет большой смысл. И я на самом деле делаю все это через SWIG, поэтому я уверен, что Quaternion для меня обернут как некий тип PyObject. Теперь осталось понять, как этим пользоваться... - person Mike; 23.10.2013