Numpy массив C API - PullRequest
       42

Numpy массив C API

8 голосов
/ 28 мая 2010

У меня есть функция C ++, возвращающая std :: vector, и я хочу использовать ее в python, поэтому я использую api C:

static PyObject *
py_integrate(PyObject *self, PyObject *args){
    ...
    std::vector<double> integral;
    cpp_function(integral);  // This changes integral
    npy_intp size = {integral.size()};
    PyObject *out = PyArray_SimpleNewFromData(1, &size, NPY_DOUBLE, &(integral[0]));
    return out;
}

Вот как я это называю из python:

import matplotlib.pyplot as plt

a = py_integrate(parameters)
print a
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(a)
print a

Что происходит: первый отпечаток в порядке, значения верны. Но когда я строю сюжет a, это не так; во второй печати я вижу очень странные значения, такие как 1E-308 1E-308 ... или 0 0 0 ... как неинициализированную память. Я не понимаю, почему первый отпечаток в порядке.

Частичное решение (не работает):

static void DeleteVector(void *ptr)
{
    std::cout << "Delete" << std::endl;
    vector * v = static_cast<std::vector<double> * >(ptr);
    delete v;
    return;
}

static PyObject *
cppfunction(PyObject *self, PyObject *args)
{
    std::vector<double> *vector = new std::vector<double>();
    vector->push_back(1.);
    PyObject *py_integral = PyCObject_FromVoidPtr(vector, DeleteVector);
    npy_intp size = {vector->size()};
    PyArrayObject *out;
    ((PyArrayObject*) out)->base = py_integral;
    return (PyObject*)(out);
}

Ответы [ 2 ]

11 голосов
/ 28 мая 2010

Ваш std::vector объект кажется локальным для этой функции. PyArray_SimpleNewFromData не делает копию данных, которые вы передаете. Он просто держит указатель. Поэтому, как только ваша функция py_integrate возвращается, вектор освобождается. Печать работает в первый раз, потому что еще ничего не записано в освобожденную память, но к тому времени, когда вы переходите к следующему отпечатку, что-то еще использует эту память, что приводит к разным значениям.

Вам необходимо создать массив NumPy, которому принадлежит собственное пространство хранения, а затем скопировать в него данные.

Или же разместите свой вектор в куче. Затем сохраните указатель на него в CObject . Предоставьте деструктор, который удаляет вектор. Затем взгляните на тип C-level PyArrayObject . У него есть PyObject * член с именем base. Храните свой CObject там. Затем, когда массив NumPy будет собирать мусор, счетчик ссылок на этот базовый объект будет уменьшен, и, если вы не взяли его копию в другом месте, ваш вектор будет удален благодаря предоставленному вами деструктору.

Fixer Верхняя

Вы забыли фактически создать PyArray. Попробуйте это:

(Вы не опубликовали DeleteVector, поэтому я могу только надеяться, что это правильно)

std::vector<double> *vector = new std::vector<double>();
vector->push_back(1.);
PyObject *py_integral = PyCObject_FromVoidPtr(vector, DeleteVector);
npy_intp size = {vector->size()};
PyObject *out = PyArray_SimpleNewFromData(1, &size, NPY_DOUBLE, &((*vector)[0]));
((PyArrayObject*) out)->base = py_integral;
return out;

Примечание: я не программист на C ++, поэтому могу только предположить, что &((*vector)[0]) работает так, как задумано, с указателем на вектор. Я знаю, что вектор перераспределяет свою область памяти, если вы ее увеличиваете, поэтому не увеличивайте его размер после получения этого указателя, иначе он больше не будет действительным.

0 голосов
/ 17 апреля 2014

Вам нужно будет сделать копию вектора, так как вектор выйдет из области видимости и память больше не будет использоваться к тому времени, когда вам это понадобится в Python (как заявлено kwatford).

Один из способов создать нужный массив Numpy (путем копирования данных):

PyObject *out = nullptr;

std::vector<double> *vector = new std::vector<double>();
vector->push_back(1.);

npy_intp size = {vector.size()};

out = PyArray_SimpleNew(1, &size, NPY_DOUBLE);

memcpy(PyArray_DATA((PyArrayObject *) out), vector.data(), vector.size());
...