我有一个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 }
};
更新
get_array函数:
#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;
}
答案 0 :(得分:1)
您无法直接用Python返回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;
请注意,我尚未完成故障的内存管理,因此您会泄漏array
。
为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对象。