return 2D-массив, созданный с помощью функции C в Python с использованием Cython

Я хочу использовать 2D-массив, созданный функцией c в python . Я спросил, как это сделать до сегодняшнего дня, и один подход, предложенный @Abhijit Pritam, заключался в использовании структур. Я реализовал его, и он работает.

c код:

 typedef struct { int arr[3][5]; } Array; Array make_array_struct() { Array my_array; int count = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 5; j++) my_array.arr[i][j] = ++count; return my_array; } 

в python у меня это:

 cdef extern from "numpy_fun.h": ctypedef struct Array: int[3][5] arr cdef Array make_array_struct() def make_array(): cdef Array arr = make_array_struct() return arr my_arr = make_array() my_arr['arr'] [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]] 

Однако было высказано предположение, что это не лучший подход к проблеме, потому что можно заставить python контролировать данные. Я пытаюсь реализовать это, но пока не могу. Это то, что у меня есть.

c код:

 int **make_array_ptr() { int **my_array = (int **)malloc(3 * sizeof(int *)); my_array[0] = calloc(3 * 5, sizeof(int)); for (int i = 1; i < 3; i++) my_array[i] = my_array[0] + i * 5; int count = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 5; j++) my_array[i][j] = ++count; return my_array; } 

питон:

 import numpy as np cimport numpy as np np.import_array() ctypedef np.int32_t DTYPE_t cdef extern from "numpy/arrayobject.h": void PyArray_ENABLEFLAGS(np.ndarray arr, int flags) cdef extern from "numpy_fun.h": cdef int **make_array_ptr() def make_array(): cdef int[::1] dims = np.array([3, 5], dtype=np.int32) cdef DTYPE_t **data = make_array_ptr() cdef np.ndarray[DTYPE_t, ndim=2] my_array = np.PyArray_SimpleNewFromData(2, &dims[0], np.NPY_INT32, data) PyArray_ENABLEFLAGS(my_array, np.NPY_OWNDATA) return my_array 

Я следовал за Force NumPy ndarray, чтобы взять на себя ответственность за свою память в Cython, которая, похоже, мне нужна. В моем случае это другое, потому что мне нужен 2D-массив, поэтому мне, вероятно, придется делать что-то по-другому, потому что, например, функция ожидает, что data будут указателем на int, и я дал ему указатель на указатель на int. Что мне нужно сделать, чтобы использовать этот подход?

Мои проблемы с struct подходом:

  1. Он ломается, как только вы хотите что-то, кроме фиксированного размера массива, без реального способа его исправления.

  2. Он полагается на неявное преобразование Китона из структур в dicts. Cython копирует данные в список Python, что не очень эффективно. Это не проблема с небольшими массивами, которые у вас есть здесь, но это глупо для больших массивов.


Я также не рекомендую 2D-массивы как указатели на указатели. Путь numpy (и большинство других разумных библиотек массивов) реализует 2D-массивы – это хранить 1D-массив и форму 2D-массива и просто использовать форму для определения того, какой индекс для доступа. Это имеет тенденцию быть более эффективным (более быстрый поиск, более быстрое распределение), а также более простой в использовании (меньший уровень распределения / освобождения для отслеживания).

Для этого измените код C на:

 int32_t *make_array_ptr() { int32_t *my_array = calloc(3 * 5, sizeof(int32_t)); int count = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 5; j++) my_array[j+i*5] = ++count; return my_array; } 

Я удалил первый цикл, который вы сразу перезаписываете. Я также изменил тип int32_t так как вы, кажется, полагаетесь на это в своем коде Cython позже.

Код Cython тогда очень близок к тому, что вы использовали:

 def make_array(): cdef np.intp_t dims[2] dims[0]=3; dims[1] = 5 cdef np.int32_t *data = make_array_ptr() cdef np.ndarray[np.int32_t, ndim=2] my_array = np.PyArray_SimpleNewFromData(2, &dims[0], np.NPY_INT32, data) PyArray_ENABLEFLAGS(my_array, np.NPY_OWNDATA) return my_array 

Основные изменения состоят в том, что я удалил некоторые роли, а также просто выделил dims как статический массив (который казался проще памяти)


Я не думаю, что это особенно легко позволить numpy обрабатывать массив указателя на указатель. Это может быть возможно путем реализации интерфейса буфера Python, но это похоже на большую работу и может быть нелегким.