Python C API 读取列表的列表,分配内存和全局变量

1 投票
1 回答
546 浏览
提问于 2025-04-18 05:13

我正在写一个模块,目的是优化一个操作,但我有几个问题。

首先

我需要把一个数组(也就是一个列表的列表)从Python传到C,我是这样做的:

long i_k;
for (int k = 0; k < n; k++) {
  PyObject *first = PyList_GetItem(streamMatrixObj, k);
  for (int i = 0; i < n; i++) {
    PyObject *second = PyList_GetItem(first, i);
    i_k = PyLong_AsLong(second);
    f[k][i] = i_k;
  }
} 

这个矩阵的大小总是 n x n。有没有更好的方法来读取和存储这个列表到C的数组里呢?

其次

我为C数组分配内存是这样做的:

void **createMatrix(uint rows, uint cols) {

  long **matrix;

  const size_t row_pointers_bytes = rows * sizeof(*matrix);
  const size_t row_elements_bytes = cols * sizeof(**matrix);
  matrix = PyObject_Malloc(row_pointers_bytes + rows * row_elements_bytes);

  if (!matrix) {
    PyErr_NoMemory();
    return NULL;
  }

  size_t i = 0;
  long *data = matrix + rows;
  for (i = 0; i < rows; i++)
    matrix[i] = data + i * cols;

  return matrix;
}

我应该用 PyMem_Malloc 还是 PyObject_Malloc 呢?我在这个问题里看到了一些解释:

有没有理由使用malloc而不是PyMem_Malloc?

但我还是不太清楚该用哪个函数。

第三

这个模块的方法会被调用很多次,所以我想把C数组保留在内存中,以避免每次都重新创建数组。比如说,我会从Python调用这个方法 myModule.deltaC(1,2,[1,2,3]),这个调用会对矩阵进行一些操作。

我能不能在使用 deltaC 函数之前先初始化数组,比如用 myModule.initMatrix([[1,2,3],[1,2,3], [1,2,3]]),然后把它保留在内存中直到程序结束?那么这个C数组就需要在模块里作为一个全局变量吗?

更新

我试着用 module.initMatrix 在全局变量中初始化C数组,但当调用 module.delta 时,访问C数组时出现了段错误(SegFault),所以我想这可能是不行,或者我遗漏了什么。

第四

有没有办法检测到Python程序结束,以便调用 PyObject_free(array) 来释放为C数组分配的内存?

1 个回答

1

首先:这基本上就是应该怎么做。

其次:我理解你应该在这里使用 PyMem_MallocPyMem_Free。而 PyObject_MallocPyObject_Free 是专门用来分配Python对象的,特别适合处理很多小对象。不过,除非你分配了大量内存或者进行了非常多的分配,否则这两者之间的区别并不会太大。

第三和第四:你可以使用 Capsules API 来存储你的C结构,并在API函数之间传递它们。与其存储一个全局矩阵,不如让你的初始化函数(initMatrix)返回一个包含矩阵的胶囊,然后把这个胶囊传递给其他函数(比如deltaC)。当这个矩阵不再被引用时,它会自动被销毁。

如果你只打算存储矩阵,另一种可能的选择是使用 numpy 数组。这些数组可以通过 numpy C API 直接在C语言中作为矩阵访问。这样你就不需要调用Python的C API函数来获取矩阵的每个元素,也不需要自己分配内存或在矩阵被销毁时手动释放内存。

撰写回答