У меня есть функция C, которая mallocs() и заполняет 2D-массив поплавков. Он "возвращает" этот адрес и размер массива. Подпись
int get_array_c(float** addr, int* nrows, int* ncols);
Я хочу назвать это из Python, поэтому я использую ctypes.
import ctypes
mylib = ctypes.cdll.LoadLibrary('mylib.so')
get_array_c = mylib.get_array_c
Я никогда не выяснял, как указать типы аргументов с помощью ctypes. Я обычно пишу оболочку python для каждой используемой функции C, и удостоверяюсь, что я получаю типы в оболочке. Массив поплавков - это матрица в порядке столбцов, и я хотел бы получить ее как numpy.ndarray. Но он довольно большой, поэтому я хочу использовать память, выделенную функцией C, а не копировать ее. (Я только что нашел этот материал PyBuffer_FromMemory в этом ответе StackOverflow: qaru.site/info/233243/...)
buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
import numpy
def get_array_py():
nrows = ctypes.c_int()
ncols = ctypes.c_int()
addr_ptr = ctypes.POINTER(ctypes.c_float)()
get_array_c(ctypes.byref(addr_ptr), ctypes.byref(nrows), ctypes.byref(ncols))
buf = buffer_from_memory(addr_ptr, 4 * nrows * ncols)
return numpy.ndarray((nrows, ncols), dtype=numpy.float32, order='F',
buffer=buf)
Кажется, это дает мне массив с правильными значениями. Но я уверен, что это утечка памяти.
>>> a = get_array_py()
>>> a.flags.owndata
False
Массив не имеет памяти. Справедливо; по умолчанию, когда массив создается из буфера, он не должен. Но в этом случае это должно быть. Когда массив numpy удален, мне бы очень хотелось, чтобы python освободил буферную память для меня. Похоже, если бы я мог заставить owndata к True, это должно сделать это, но owndata не настраивается.
Неудовлетворительные решения:
-
Сделать вызывающим агентом get_array_py() ответственным за освобождение памяти. Это супер раздражает; вызывающий должен иметь возможность обрабатывать этот массив numpy точно так же, как любой другой массив numpy.
-
Скопируйте исходный массив в новый массив numpy (со своей собственной, отдельной памятью) в get_array_py, удалите первый массив и освободите память внутри get_array_py(). Верните копию вместо исходного массива. Это раздражает, потому что это должна быть ненужная копия памяти.
Есть ли способ сделать то, что я хочу? Я не могу сам изменить функцию C, хотя я мог бы добавить в библиотеку еще одну функцию C, если это полезно.