python - 从 C 扩展返回 C 字符串数组

标签 python c python-c-api

我有一个返回字符串数组的 C 函数。如何以 Python C 扩展的形式调用它,将数组返回给调用 Python 函数? (我是 Python C 扩展的新手,并且对扩展的经验很少)

这是我尝试过的定义:

static PyObject* _get_array(PyObject* self, PyObject* args)
{
    int64_t value;
    int init_level;
    int final_level;

    if(!PyArg_ParseTuple(args, "Lii", &value, &init_level, &final_level))
        return NULL;

    // returning the array as a Python object by o
    return Py_BuildValue("o", _get_array(value, init_level, final_level));
}

和方法def:

static PyMethodDef array_methods[] = {
    { "get_array", _get_array, METH_VARARGS, "Returns a string array"},
    { NULL, NULL, 0, NULL }
};

更新

获取数组函数:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <Python.h>

char **get_array(int64_t value, int init_level, int final_level) {

  int SHIFTS []= {44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0};

  long count =  1 << (4* (final_level - init_level));
  char** t_array;
  t_array = malloc(sizeof(char*)*count);

  int shift_coff = 11 -(final_level-init_level);
  int64_t base = (value << SHIFTS[shift_coff]);

  for (long i=0; i < count; i++){
    t_array[i] = malloc((4+final_level)*sizeof(char));
    sprintf(t_array[i], "%llX", (base + i));
  }

  return t_array;
}

最佳答案

你不能直接返回你的char**,因为Python只理解PyObject*类型的对象(因为它包含处理引用计数和识别所需的信息)方式)。因此,您必须创建一个合适的 Python 对象。最简单的选项是字符串列表。下一个最简单的是使用字符串类型的 numpy 数组(您可以轻松做到这一点,因为所有字符串的长度都相同)。这些都没有直接的 Py_BuildValue 转换,因此您必须自己编写循环。

<小时/>

对于字符串列表,您只需使用 PyList_New 创建列表,然后使用 PyList_SetItem 逐个元素进行遍历即可:

char** array = get_array(value, init_level, final_level);
PyObject* list = PyList_New(1 << (4* (final_level - init_level)));
if (!list) return NULL;

for (int i=0; i<(1 << (4* (final_level - init_level))); ++i) {
    PyObject* item = PyBytes_FromStringAndSize(array[i],(4+final_level));
    if (!item) goto failed;

    if (PyList_SetItem(list,i,item) != 0) {
        Py_DECREF(item);
        goto failed;
    }

    free(array[i]); // deallocate array as we go
}
free(array);

// returning the array as a Python object by o
return list;

failed:
Py_DECREF(list);
// also deallocate the rest of array?
return NULL;

请注意,我尚未完成失败的内存管理,因此您将泄漏数组

<小时/>

对于 numpy 数组,您分配一个具有正确字符串类型的数组,然后将数据复制到其中

char** array = get_array(value, init_level, final_level);

// create an "Sx" dtype, where x is a suitable number
PyArray_Descr *desc = PyArray_DescrNewFromType(NPY_STRING);
desc->elsize = (4+final_level);

npy_intp array_length[] = {1 << (4* (final_level - init_level))};
PyObject* nparray = PyArray_SimpleNewFromDescr(1,array_length,desc);
if (!nparray) return NULL; // clean up array too

for (int i=0; i<(1 << (4* (final_level - init_level))); ++i) {
    char* data = PyArray_GETPTR1((PyArrayObject*)nparray,i);

    // copy data
    for (int j=0; j<(4+final_level); ++j) {
        data[j] = array[i][j];
    }

    free(array[i]); // deallocate array as we go
}
free(array);

// returning the array as a Python object by o
return nparray;

同样,并非所有错误处理都是完美的。要使此示例正常工作,您必须在模块初始化函数中调用import_array()

<小时/>

在这两种情况下,您最好不要在 get_array 中分配内存,而是直接写入 Python 对象。

关于python - 从 C 扩展返回 C 字符串数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55839131/

相关文章:

python - Pandas 嵌套的 groupby 给出了意想不到的结果

python - 类型错误 : unorderable types: str() < int()

c - 为什么使用偏移密码加密后文件大小变小了?

Python 对象未使用 C API 完全初始化

python - 如何在嵌入Python代码的jinja2模板中正确调用和使用变量?

python - 如何查找模型是否是从 Django Admin 或其他地方保存的

c - 带 EOF 的额外循环

多个文件之间的函数类型冲突

numpy - 从 numpy C API 读取许多值

Python C API unicode 参数