[英]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);
}
}
說明:
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對象所需的所有內容。 我承認我在調查過程中使用了其中一名成員。 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;
如您所見,結構的前兩個元素與PyObject和PyArrayObject相同,但可以使用此定義來解決其他元素。 直接訪問這些元素很誘人,但這是一個非常糟糕和危險的做法,而不是強烈勸阻 。 您必須使用宏,而不必擔心所有這些結構中的細節和元素。 我以為你可能會對一些內部人員感興趣。
請注意,所有PyArrayObject宏都記錄在http://docs.scipy.org/doc/numpy/reference/c-api.array.html中。
例如,可以使用宏
PyArray_SIZE(PyArrayObject *)
獲取PyArrayObject的大小
最后,一旦你知道它,這是非常簡單和合乎邏輯的:-)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.