python - 使用 Cython 将 C 函数创建的二维数组返回到 Python

标签 python c cython

我想使用 python 中的 c 函数创建的二维数组。我之前问过如何做到这一点 today @Abhijit Pritam 建议的一种方法是使用结构。我实现了它并且它确实有效。

C 代码:

typedef struct {
  int arr[3][5];
} Array;

Array make_array_struct() {
  Array my_array;
  int count = 0;
  for (int i = 0; i < 3; i++)
    for (int j = 0; j  < 5; j++)
      my_array.arr[i][j] = ++count;
  return my_array;
}

在 python 中我有这个:

cdef extern from "numpy_fun.h":
    ctypedef struct Array:
        int[3][5] arr
    cdef Array make_array_struct()

def make_array():
    cdef Array arr = make_array_struct()
    return arr

my_arr = make_array()
my_arr['arr']
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]

然而,有人认为这不是解决问题的最佳方法,因为可以让 python 控制数据。我正在尝试实现这一点,但到目前为止我还没有做到。这就是我的。

C 代码:

int **make_array_ptr() {
  int **my_array = (int **)malloc(3 * sizeof(int *));
  my_array[0] = calloc(3 * 5, sizeof(int));
  for (int i = 1; i < 3; i++)
    my_array[i] = my_array[0] + i * 5;
  int count = 0;
  for (int i = 0; i < 3; i++)
    for (int j = 0; j < 5; j++)
      my_array[i][j] = ++count;
  return my_array;
}

python :

import numpy as np
cimport numpy as np

np.import_array()

ctypedef np.int32_t DTYPE_t

cdef extern from "numpy/arrayobject.h":
    void PyArray_ENABLEFLAGS(np.ndarray arr, int flags)

cdef extern from "numpy_fun.h":
    cdef int **make_array_ptr()

def make_array():
    cdef int[::1] dims = np.array([3, 5], dtype=np.int32)
    cdef DTYPE_t **data = <DTYPE_t **>make_array_ptr()
    cdef np.ndarray[DTYPE_t, ndim=2] my_array = np.PyArray_SimpleNewFromData(2, &dims[0], np.NPY_INT32, data)
    PyArray_ENABLEFLAGS(my_array, np.NPY_OWNDATA)
    return my_array

我正在关注 Force NumPy ndarray to take ownership of its memory in Cython 这似乎是我需要做的。在我的例子中,它是不同的,因为我需要二维数组,所以我可能不得不做一些不同的事情,因为例如函数期望 data 是一个指向 int 的指针,我给了它一个指向指向 int 的指针。 我需要做什么才能使用这种方法?

最佳答案

我对 struct 方法的问题是:

  1. 只要您想要任何固定大小的数组,它就会中断,而且没有真正的修复方法。

  2. 它依赖于 Cython 从结构到字典的隐式转换。 Cython 将数据复制到 Python 列表,这不是非常有效。这对于您这里的小阵列来说不是问题,但对于较大的阵列来说就很愚蠢了。


我也不太推荐将二维数组作为指向指针的指针。 numpy(和大多数其他合理的数组库)实现二维数组的方式是存储一维数组和二维数组的形状,并仅使用形状来计算要访问的索引。这往往更高效(更快的查找、更快的分配)并且也更易于使用(更少的分配/释放来跟踪)。

为此,将 C 代码更改为:

int32_t *make_array_ptr() {
  int32_t *my_array = calloc(3 * 5, sizeof(int32_t));
  int count = 0;
  for (int i = 0; i < 3; i++)
    for (int j = 0; j < 5; j++)
      my_array[j+i*5] = ++count;
  return my_array;
}

我删除了您立即覆盖的第一个循环。我还更改了 int32_t 的类型,因为您稍后似乎会在您的 Cython 代码中依赖它。

Cython 代码非常接近您使用的代码:

def make_array():
    cdef np.intp_t dims[2] 
    dims[0]=3; dims[1] = 5
    cdef np.int32_t *data = make_array_ptr()
    cdef np.ndarray[np.int32_t, ndim=2] my_array = np.PyArray_SimpleNewFromData(2, &dims[0], np.NPY_INT32, data)
    PyArray_ENABLEFLAGS(my_array, np.NPY_OWNDATA)
    return my_array

主要的变化是我删除了一些强制类型转换,并且只是将 dims 分配为静态数组(这看起来比 memoryviews 更简单)


我不认为允许 numpy 处理指针到指针数组特别容易。通过实现 Python 缓冲区接口(interface)可能是可能的,但这似乎需要大量工作,而且可能并不容易。

关于python - 使用 Cython 将 C 函数创建的二维数组返回到 Python,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49539070/

相关文章:

c - 为什么 int 而不是 unsigned int 用于 C 和 C++ for 循环?

python - 如何将多个单词列表转换为 Pandas 数据框?

python - 如何在django中将普通数字转换为阿拉伯数字

c - 不小心创建了巨大的文件

c - 以什么方式,fork() 系统调用生成子进程?

带有自定义共享库的 Python C 扩展链接

python - 使用 Cython 定义 Python 类方法

Cython 和交叉编译

python - 使用 Python 从数组中打印 N 次

python - 如何识别某个图像何时消失