Обтекание функции C в Cython и NumPy
Я бы хотел вызвать функцию C из Python, чтобы манипулировать некоторыми массивами NumPy. Функция такова:
void c_func(int *in_array, int n, int *out_array);
где результаты представлены в out_array, размер которых я знаю заранее (а не моя функция, фактически). Я пытаюсь сделать в соответствующем .pyx файле следующее, чтобы передать входной сигнал функции из массива NumPy и сохранить результат в массиве NumPy:
def pyfunc(np.ndarray[np.int32_t, ndim=1] in_array):
n = len(in_array)
out_array = np.zeros((512,), dtype = np.int32)
mymodule.c_func(<int *> in_array.data, n, <int *> out_array.data)
return out_array
Но я получаю
"Python objects cannot be cast to pointers of primitive types"
ошибка для назначения вывода. Как это сделать?
(Если мне требуется, чтобы вызывающий абонент Python выделял правильный выходной массив, я могу сделать
def pyfunc(np.ndarray[np.int32_t, ndim=1] in_array, np.ndarray[np.int32_t, ndim=1] out_array):
n = len(in_array)
mymodule.cfunc(<int *> in_array.data, n, <int*> out_array.data)
Но могу ли я сделать это так, чтобы вызывающему абоненту не приходилось предварительно выделять выходной массив соответствующего размера?
Ответы
Ответ 1
Вы должны добавить cdef np.ndarray
перед назначением out_array
:
def pyfunc(np.ndarray[np.int32_t, ndim=1] in_array):
cdef np.ndarray out_array = np.zeros((512,), dtype = np.int32)
n = len(in_array)
mymodule.c_func(<int *> in_array.data, n, <int *> out_array.data)
return out_array
Ответ 2
Вот пример, как манипулировать массивами NumPy, используя код, написанный на C/С++ через ctypes.
Я написал небольшую функцию в C, взяв квадрат чисел из первого массива и записывая результат во второй массив. Число элементов задается третьим параметром. Этот код скомпилирован как общий объект.
squares.c скомпилирован в squares.so:
void square(double* pin, double* pout, int n) {
for (int i=0; i<n; ++i) {
pout[i] = pin[i] * pin[i];
}
}
В python вы просто загружаете библиотеку с помощью ctypes и вызываете функцию. Указатели массива получают из интерфейса ctypes NumPy.
import numpy as np
import ctypes
n = 5
a = np.arange(n, dtype=np.double)
b = np.zeros(n, dtype=np.double)
square = ctypes.cdll.LoadLibrary("./square.so")
aptr = a.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
bptr = b.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
square.square(aptr, bptr, n)
print b
Это будет работать для любой c-библиотеки, вам просто нужно знать, какие типы аргументов пройти, возможно, перестроить c-structs в python с помощью ctypes.