使用Python处理C扩展时,将数据从C ++数组或vector
移动到PyObject
/ PyList
的最佳方法是什么,以便将其返回给Python?
我现在使用的方法看起来有点笨重。我循环遍历每个元素并调用Py_BuildValue
将值附加到PyList
。
是否有与memcpy
类似的内容?
答案 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 docs和here you can find an example of usage将std::vector
转换为python::list
。