经过几个小时的阅读多页文档并深入研究 numpy 包含文件后,我终于成功地理解了它的工作原理。由于我花费了大量时间寻找这些确切的解释,因此我提供以下文本以避免任何人浪费时间。
我重复一下这个问题:
如何将 numpy 数组列表从 Python 传输到 C
(我还假设您知道如何在 Python 中编译、链接和导入 C 模块)
通过一个Numpy从 Python 到 C 的数组相当简单,只要将其作为 C 函数中的参数传递即可。你只需要在Python中做这样的事情
from numpy import array
from ctypes import c_long
values = array([1.0, 2.2, 3.3, 4.4, 5.5])
my_c_func(values.ctypes.data_as(c_void_p), c_long(values.size))
C 代码可能如下所示:
void my_c_func(double *value, long size)
{
int i;
for (i = 0; i < size; i++)
printf("%ld : %.10f\n", i, values[i]);
}
这很简单......但是如果我有可变数量的数组怎么办?当然,我可以使用解析函数参数列表的技术(许多例子堆栈溢出),但我想做一些不同的事情。
我想将所有数组存储在一个列表中,并将该列表传递给 C 函数,然后让 C 代码处理所有数组。
事实上,它非常简单、容易且连贯......一旦您了解它是如何完成的!有一个非常简单的事实需要记住:
列表/元组/字典的任何成员都是 Python 对象...在代码的 C 端!
你不能指望直接通过pointer正如我最初错误地认为的那样。一旦说过,这听起来很简单:-)不过,让我们编写一些Python代码:
from numpy import array
my_list = (array([1.0, 2.2, 3.3, 4.4, 5.5]),
array([2.9, 3.8. 4.7, 5.6]))
my_c_func(py_object(my_list))
好吧,您不需要更改列表中的任何内容,但您需要指定将列表作为PyObject争论。
以下是如何在 C 中访问所有这些内容。
void my_c_func(PyObject *list)
{
int i, n_arrays;
// Get the number of elements in the list
n_arrays = PyObject_Length(list);
for (i = 0; i LT n_arrays; i++)
{
PyArrayObject *elem;
double *pd;
elem = PyList_GetItem(list,
i);
pd = PyArray_DATA(elem);
printf("Value 0 : %.10f\n", *pd);
}
}
解释 :
- 该列表作为指向PyObject
- 我们使用以下方法从列表中获取数组的数量PyObject_Length()功能。
-
PyList_GetItem()
always返回一个PyObject(实际上是一个void *
)
- 我们使用以下方法检索指向数据数组的指针
PyArray_DATA()
macro.
通常情况下,PyList_GetItem()
返回一个py对象 *,但是,如果你看看Python.h and ndarraytypes.h,您会发现它们都定义为(我已经扩展了宏!):
typedef struct _object {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
And the PyArray对象……一模一样。不过,在这个级别上它是完全可以互换的。 ob_type 的内容对于这两个对象都是可访问的,并且包含操作任何通用 Python 对象所需的所有内容。我承认我在调查过程中使用了其中一名成员。这struct member tp_name是包含对象名称的字符串...以明文形式表示;相信我,这很有帮助!这就是我发现每个列表元素包含的内容的方式。
虽然这些结构不包含任何其他内容,但我们如何访问 this 的指针ndarray目的 ?简单地使用对象宏...它使用扩展结构,允许编译器知道如何访问附加对象的元素,在ob_type指针。这PyArray_DATA()
宏定义为:
#define PyArray_DATA(obj) ((void *)((PyArrayObject_fields *)(obj))->data)
在那里,它正在投射PyArayObject *
as a PyArrayObject_fields *
这个最新的结构很简单(简化并扩展了宏!):
typedef struct tagPyArrayObject_fields {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
char *data;
int nd;
npy_intp *dimensions;
npy_intp *strides;
PyObject *base;
PyArray_Descr *descr;
int flags;
PyObject *weakreflist;
} PyArrayObject_fields;
正如您所看到的,该结构的前两个元素与PyObject and PyArray对象,但附加元素可以是已解决使用这个定义。直接访问这些元素很诱人,但这是一种非常糟糕且危险的做法,不仅仅是强烈劝阻。您必须使用宏,而不用担心所有这些结构中的细节和元素。我只是觉得你可能会对某些人感兴趣内部结构.
请注意,所有PyArray对象宏记录在http://docs.scipy.org/doc/numpy/reference/c-api.array.html http://docs.scipy.org/doc/numpy/reference/c-api.array.html
例如,一个的大小PyArray对象可以使用宏来获取PyArray_SIZE(PyArrayObject *)
最后,一旦您了解了它,它就非常简单且符合逻辑:-)