python - 如何使用 Python C API 在已编译的 C 模块中实现 Python 数组

标签 python c arrays

我正在尝试尽可能高效地将 double 组从 C 库发送到 Python。 与此库通信的代码是由另一家公司创建的(它包括许多方法、异常等),但是这个特定函数创建一个列表并从 C 中的每个项目中插入一个 Python 对象数组,如果你关心速度,这是非常低效的。

下面是编译用于创建 python 模块的 C 代码片段:

static PyObject* foo(PyObject* self, PyObject* args) {

    double *val = 0;
    //more variables

    //Note that this uses the Python C API PyArg_ParseTuple to handle the parameters
    if (! PyArg_ParseTuple(args, "ii", &listID, &size)) {
        //send exception
    }

    //some code here that allocates an array to hold "val" and calls the C library

    PyList_New(size);
    for(i = 0; i < size; i++) {
        PyList_SET_ITEM(retData, i, Py_BuildValue("d", val[i]));
    }

    //free resources, return the Python object
}

我发现 Python array可能有用,还有适合多进程的额外好处。

如果Python数组按照我的想象工作,我可以在Python中分配数组,然后C库就填充它

from cpython cimport array
import array
from dalibrary import dafunction

cdef array.array a = array.array('d', [])
array.resize(a, 1000)

dafunction(array, 1000)  #  In a very "C" style, the array would be filled with values

print(array)

问题是我没有找到有关使用 Python 数组所需的 C 代码的文档。至少不使用Python C API。

注意:我知道 ctypes,但这意味着重写整个模块,如果可能的话,我宁愿不这样做(但缺乏文档可能会驱使我这样做)

似乎有人已经问过类似的问题 here ,但仍未解决

结果:

我设法执行了我想要的操作(正如您可能在其中一个答案中看到的那样),甚至对数组(多进程数组)使用多线程,但令我惊讶的是,它实际上比使用据说效率低下的方法要慢一些(但是健壮的)IPC 方法,例如带有 Python 列表的队列。

由于使用Python API很困难,而且它给我带来的改进为零,我认为对社区来说最好的答案是使用ctypes的建议。我会保留我的答案以供引用。也许发送大量内存的人可能会从中受益。

最佳答案

要将数据从 C 转换为 Python,需要做很多事情。首先你应该决定谁来处理内存。生成数组的是 C 代码还是 Python?如果数组在很多地方共享,并且在 Python 不知情的情况下在 C 端被删除,Python 将崩溃。或相反亦然。

因此复制数组可能不是一个坏主意。

话虽如此,您可以编写一个简单的 C 函数

struct Array {
   int size;
   int* data;
}

Array get_my_array() {
    //...
    return {size, val};
}

将其编译为动态库 (my_lib.so) 并使用 Ctypes 对其进行包装(它是用于访问外部函数的标准 Python 库)。 您需要描述Array返回类型:

from ctypes import Structure, POINTER, c_int, CDLL, find_library, pointer

class Array(Structure):
    __fields__ = [("size", c_int), ("data", POINTER(c_int))]


my_lib = CDLL(find_library("my_lib"))
my_lib.get_my_array.restype = Array

现在您可以获取数组并访问其数据和大小(并手动保护自己免受越界访问)。

例如,您也可以将其传递给 Numpy。幸运的是,这里的答案中有一个相当完整的示例How to create n-dim numpy array from a pointer? 仔细阅读,别忘了清理内存。

请注意,您也可以以相反的方式进行操作。如果您在 Python 中知道要创建的数组的大小并且只需要 C 代码来填充它,则可以在 CTypes 中创建它,并将其传递给接受指针和大小的 C 函数。

ArrayType = c_int * size
array = ArrayType()

my_lib.populate_array(pointer(array), size)  # left as an exercise

Ctypes 非常方便,当您了解 C 语言时,它会变得很有意义。

关于python - 如何使用 Python C API 在已编译的 C 模块中实现 Python 数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59901282/

相关文章:

python - 将峰彼此等距分开

python - 正则表达式匹配字符串中以特定字符开头和结尾的所有单词

c - 如何将二维矩阵作为参数传递给函数?

python - 如何将列表的列表与Python中的任何常见元素结合起来?

python - 更改 is_superuser Django 的 verbose_name

jquery - Javascript 按值对数组进行排序

C 从结构体数组中释放内存

sql - 在 PostgreSQL 中组合两个查询

c - 汇编中无符号 32 位整数的左移

c - for循环内外变量初始化的区别