簡體   English   中英

從Python調用C:傳遞numpy指針列表

[英]Calling C from Python: passing list of numpy pointers

我有一個可變數量的numpy數組,我想傳遞給C函數。 我設法傳遞每個單獨的數組(使用<ndarray>.ctypes.data_as(c_void_p) ),但數組的數量可能會有很大差異。

我以為我可以在列表中傳遞所有這些“指針”並在C代碼中使用PyList_GetItem()函數。 它就像一個魅力,除了所有元素的值不是我作為函數參數傳遞時通常得到的指針。

但是,如果我有:

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包含文件后,我終於明白了它是如何工作的。 由於我花了很多時間尋找這些確切的解釋,我提供以下文字作為避免任何人浪費時間的方法。

我重復一下這個問題:

如何將一個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]);
}

這很簡單......但是如果我有一個可變數量的數組怎么辦? 當然,我可以使用解析函數參數列表的技術( Stackoverflow中的許多示例),但我想做一些不同的事情。

我想將所有數組存儲在一個列表中,並將此列表傳遞給C函數,讓C代碼處理所有數組。

事實上,它非常簡單,容易和連貫......一旦你理解它是如何完成的! 只需要記住一個非常簡單的事實:

list / tuple / dictionary中的任何成員都是Python對象......在代碼的C端!

我不能指望像我最初那樣直接傳遞指針 ,並且錯誤地認為。 一旦說過,聽起來很簡單:-)雖然,讓我們寫一些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.hndarraytypes.h ,你會發現它們都定義為(我擴展了宏!):

typedef struct _object {
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

PyArrayObject ......完全一樣。 雖然,它在這個級別上完全可以互換。 ob_type的內容可供兩個對象訪問,並包含操作任何通用Python對象所需的所有內容。 我承認我在調查過程中使用了其中一名成員。 struct member tp_name是包含對象名稱的字符串...以明文形式; 相信我,它有所幫助! 這就是我發現每個列表元素包含的內容。

雖然這些結構不包含任何其他內容,但我們如何才能訪問此ndarray對象的指針? 簡單地使用對象宏...它使用擴展結構,允許編譯器知道如何在ob_type指針后面訪問附加對象的元素。 PyArray_DATA()宏定義為:

#define PyArray_DATA(obj) ((void *)((PyArrayObject_fields *)(obj))->data)

在那里,它將PyArayObject *作為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;

如您所見,結構的前兩個元素與PyObjectPyArrayObject相同,但可以使用此定義來解決其他元素。 直接訪問這些元素很誘人,但這是一個非常糟糕和危險的做法,而不是強烈勸阻 您必須使用宏,而不必擔心所有這些結構中的細節和元素。 我以為你可能會對一些內部人員感興趣。

請注意,所有PyArrayObject宏都記錄在http://docs.scipy.org/doc/numpy/reference/c-api.array.html中。

例如,可以使用宏PyArray_SIZE(PyArrayObject *)獲取PyArrayObject的大小

最后,一旦你知道它,這是非常簡單和合乎邏輯的:-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM