numpy 数组 C API

发布于 2024-09-03 09:48:10 字数 1390 浏览 5 评论 0原文

我有一个返回 std::vector 的 C++ 函数,我想在 python 中使用它,所以我使用 C numpy api:

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);
}

I have a C++ function returning a std::vector and I want to use it in python, so I'm using the C numpy api:

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;
}

Here's how I call it from 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

What happens is: The first print is ok, the values are correct. But when I plot a they are not; in the second print I see very strange values like 1E-308 1E-308 ... or 0 0 0 ... as an uninitialized memory. I don't understand why the first print is ok.

Partial solution (not working):

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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

何以心动 2024-09-10 09:48:10

您的 std::vector 对象似乎是该函数的本地对象。 PyArray_SimpleNewFromData 不会复制您传递给它的数据。它只保留一个指针。因此,一旦 py_integrate 函数返回,向量就会被释放。第一次打印可以工作,因为还没有任何内容覆盖已释放的内存,但是当您进行下一次打印时,其他东西已经使用了该内存,导致值不同。

您需要创建一个拥有自己存储空间的 NumPy 数组,然后将数据复制到其中。

或者,在堆上分配向量。然后将指向它的指针存储在 CObject 中。提供删除向量的析构函数。然后,看一下 C 级 PyArrayObject 类型。它有一个名为 basePyObject * 成员。将您的 CObject 存储在那里。然后,当 NumPy 数组被垃圾收集时,该基础对象的引用计数将减少,并且假设您没有在其他地方获取它的副本,由于您提供的析构函数,您的向量将被删除。

Fixer-upper

您忘记实际创建 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]) 按预期使用指向向量的指针。我确实知道,如果您增大向量,向量会重新分配其存储区域,因此在获取该指针后不要增加其大小,否则它将不再有效。

Your std::vector object appears to be local to that function. PyArray_SimpleNewFromData does not make a copy of the data you pass it. It just keeps a pointer. So once your py_integrate function returns, the vector is deallocated. The print works the first time because nothing has written over the freed memory yet, but by the time you get to the next print, something else has used that memory, causing the values to be different.

You need to make a NumPy array that owns its own storage space and then copy the data into it.

Alternatively, allocate your vector on the heap. Then store a pointer to it in a CObject. Provide a destructor that deletes the vector. Then, take a look at the C-level PyArrayObject type. It has a PyObject * member called base. Store your CObject there. Then when the NumPy array is garbage collected, the reference count on this base object will be decremented, and assuming you haven't taken a copy of it elsewhere, your vector will be deleted thanks to the destructor you provided.

Fixer-upper

You forgot to actually create the PyArray. Try this:

(You didn't post DeleteVector, so I can only hope that it's right)

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;

Note: I'm not a C++ programmer, so I can only assume that &((*vector)[0]) works as intended with a pointer to a vector. I do know that the vector reallocate its storage area if you grow it, so don't increase its size after getting that pointer or it won't be valid anymore.

天涯沦落人 2024-09-10 09:48:10

您将需要复制向量,因为向量将超出范围,并且当您在 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());

You will need to make a copy of the vector, since the vector will go out of scope and the memory will no longer be usable by the time you need it in Python (as stated by kwatford).

One way to make the Numpy array you need (by copying the data) is:

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());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文