将C ++缓冲区复制到Python的最佳方法

时间:2015-02-17 10:40:05

标签: python c++ c

使用Python处理C扩展时,将数据从C ++数组或vector移动到PyObject / PyList的最佳方法是什么,以便将其返回给Python?

我现在使用的方法看起来有点笨重。我循环遍历每个元素并调用Py_BuildValue将值附加到PyList

是否有与memcpy类似的内容?

3 个答案:

答案 0 :(得分:2)

如果你的向量包含同质数值数据,那么最好创建一个array.array来将值传递给Python。

与列表不同,数组在内部将其值存储为本机C值的连续数组,但在其他方面提供了类似Pythonic列表的接口。它使用最小化内存占用,使您能够实际使用单个memcpy调用来有效地传输数据。这是一个例子:

PyObject *
vec_to_array(std::vector<double>& vec)
{
    static PyObject *single_array;
    if (!single_array) {
        PyObject *array_module = PyImport_ImportModule("array");
        if (!array_module)
            return NULL;
        PyObject *array_type = PyObject_GetAttrString(array_module, "array");
        Py_DECREF(array_module);
        if (!array_type)
            return NULL;
        // array.array('d', [0.0])
        single_array = PyObject_CallFunction(array_type, "c[d]", 'd', 0.0);
        Py_DECREF(array_type);
        if (!single_array)
            return NULL;
    }

    // extra-fast way to create an empty array of count elements:
    //   array = single_element_array * count
    PyObject *pysize = PyLong_FromSsize_t(vec.size());
    if (!pysize)
        return NULL;
    PyObject *array = PyNumber_Multiply(single_array, pysize);
    Py_DECREF(pysize);
    if (!array)
        return NULL;

    // now, obtain the address of the array's buffer
    PyObject *buffer_info = PyObject_CallMethod(array, "buffer_info", "");
    if (!buffer_info) {
        Py_DECREF(array);
        return NULL;
    }
    PyObject *pyaddr = PyTuple_GetItem(buffer_info, 0);
    void *addr = PyLong_AsVoidPtr(pyaddr);

    // and, finally, copy the data.
    if (vec.size())
        memcpy(addr, &vec[0], vec.size() * sizeof(double));

    return array;
}

使用模板特化来增强此功能以支持其他原始类型,这是读者的练习。

答案 1 :(得分:1)

在将每个元素添加到序列之前,必须从每个元素构造一个PyObject

所以你必须逐个添加它们,或者全部转换它们,然后从PyObject[]传递给构造函数。

我猜第二种方法稍快一些,因为它不必在每次添加后调整序列的成员变量。

答案 2 :(得分:0)

如果您可以将Boost安装为依赖项,那么您可以利用boost::python::list类来自动进行转换。 Here are the docshere you can find an example of usagestd::vector转换为python::list