python - 从 Python : passing list of numpy pointers 调用 C

标签 python list numpy void-pointers multidimensional-array

我有可变数量的 numpy 数组,我想将它们传递给 C 函数。我设法传递了每个单独的数组(使用 <ndarray>.ctypes.data_as(c_void_p) ),但数组的数量可能会有很大差异。

我以为我可以在列表中传递所有这些“指针”并使用 PyList_GetItem() C 代码中的函数。它就像一个魅力,除了所有元素的值不是我通常在作为函数参数传递时得到的指针。

虽然,如果我有:

from numpy import array
from ctypes import py_object

a1 = array([1., 2., 3.8])
a2 = array([222.3, 33.5])

values = [a1, a2]

my_cfunc(py_object(values), c_long(len(values)))

我的 C 代码看起来像:
void my_cfunc(PyObject *values)
{
    int i, n;

    n = PyObject_Length(values)
    for(i = 0; i < n; i++)
    {
        unsigned long long *pointer;
        pointer = (unsigned long long *)(PyList_GetItem(values, i);
        printf("value 0 : %f\n", *pointer);
    }
}

打印的值都是0.0000

我尝试了很多不同的解决方案,使用 ctypes.byref() , ctypes.pointer()等。但我似乎无法检索真正的指针值。我什至对 c_void_p() 转换的值有印象被截断为 32 位...

虽然有很多关于将 numpy 指针传递给 C 的文档,但我在 Python 列表中没有看到任何关于 c_types 的内容(我承认这可能看起来很奇怪......)。

有什么线索吗?

最佳答案

在花了几个小时阅读许多页面的文档和挖掘 numpy 包含文件之后,我终于设法准确地理解了它是如何工作的。由于我花费了大量时间来寻找这些确切的解释,因此我提供以下文本作为避免任何人浪费时间的一种方式。
我重复这个问题:

How to transfer a list of numpy arrays, from Python to 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]);
}
这很简单......但是如果我有可变数量的数组怎么办?当然,我可以使用解析函数参数列表的技术(Stackoverflow 中有很多示例),但我想做一些不同的事情。
我想将我所有的数组存储在一个列表中,并将这个列表传递给 C 函数,让 C 代码处理所有数组。
事实上,它非常简单、容易且连贯……一旦您了解它是如何完成的!只需记住一个非常简单的事实:

Any member of a list/tuple/dictionary is a Python object... on the C side of the code !


您不能期望像我最初错误地认为的那样直接传递指针。曾经说过,这听起来很简单 :-) 不过,让我们编写一些 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() 总是 返回一个 PyObject(实际上是一个 void * )
  • 我们使用 PyArray_DATA() 检索指向数据数组的指针。宏。

  • 通常情况下,PyList_GetItem()返回一个 PyObject *,但是,如果你查看 Python.h 和 ndarraytypes.h,你会发现它们都被定义为(我已经扩展了宏!):
    typedef struct _object {
        Py_ssize_t ob_refcnt;
        struct _typeobject *ob_type;
    } PyObject;
    
    而 PyArrayObject... 是完全一样的。不过,在这个级别上它是完全可以互换的。两个对象都可以访问 ob_type 的内容,并包含操作任何通用 Python 对象所需的一切。我承认我在调查期间曾使用过其中一名成员。结构成员 tp_name 是包含对象名称的字符串……以明文形式显示;相信我,它有帮助!这就是我发现每个列表元素包含的内容的方式。
    虽然这些结构不包含任何其他内容,但我们如何访问这个 ndarray 对象的指针?只需使用对象宏......它使用扩展结构,允许编译器知道如何在 ob_type 指针后面访问附加对象的元素。 PyArray_DATA()宏定义为:
    #define PyArray_DATA(obj) ((void *)((PyArrayObject_fields *)(obj))->data)
    
    在那里,它正在转换 PyArayObject *作为 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 和 PyArrayObject 相同,但可以使用此定义处理其他元素。直接访问这些元素很诱人,但这是一种非常糟糕和危险的做法,强烈劝阻 .您必须宁愿使用宏,并且不要打扰所有这些结构中的细节和元素。我只是想你可能会对一些内部人员感兴趣。

    Note that all PyArrayObject macros are documented in http://docs.scipy.org/doc/numpy/reference/c-api.array.html

    For instance, the size of a PyArrayObject can be obtained using the macro PyArray_SIZE(PyArrayObject *)


    最后,一旦你知道它就非常简单和合乎逻辑:-)

    关于python - 从 Python : passing list of numpy pointers 调用 C,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26384129/

    相关文章:

    python - 对于小型/大型 numpy 数组,释放的处理方式是否不同?

    python - 将 .pyc 文件反编译为 Python 3.2 的脚本

    python - 在 Python 中创建 "reversed"列表的最佳方法?

    python - 从列表中删除重复项,但考虑元素的类型并保留顺序

    python - 使用 Python 如何从 Excel 文件获取输入、定义函数并在该 Excel 文件的新工作表中生成输出?

    python - NumPy 随机种子产生不同的随机数

    Python 重复列表

    python - 如何获取 Telegram channel (超过 200 个)成员的列表

    pandas - 如何按列表中的值对数据框进行排序

    python - 使用元素求幂加速嵌套 for 循环