Получение данных из массива ctypes в numpy - PullRequest
27 голосов
/ 04 декабря 2010

Я использую библиотеку C в Python (через ctypes) для запуска серии вычислений. На разных этапах работы я хочу получить данные в Python, а именно numpy массивы.

Обертка, которую я использую, возвращает два разных типа данных массива (что представляет особый интерес для меня):

  • ctypes Массив : Когда я делаю type(x) (где x - массив ctypes, я получаю взамен <class 'module_name.wrapper_class_name.c_double_Array_12000'>. Я знаю, что эти данные являются копией внутренних данных из документации, и я могу легко получить их в массив numpy:

    >>> np.ctypeslib.as_array(x)
    

Возвращает массив данных 1D numpy.

  • ctype указатель на данные : В этом случае из документации библиотеки я понимаю, что получаю указатель на данные, хранящиеся и используемые непосредственно в библиотеке. Когда я делаю type(y) (где у указатель) я получаю <class 'module_name.wrapper_class_name.LP_c_double'>. В этом случае я все еще могу индексировать данные вроде y[0][2], но я смог получить их только через супер неловко:

    >>> np.frombuffer(np.core.multiarray.int_asbuffer(
        ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize))
    

Я нашел это в старом numpy списке рассылки ветке от Travis Oliphant , но не в документации numpy. Если вместо этого подхода я попробую, как указано выше, я получу следующее:

>>> np.ctypeslib.as_array(y)
...
...  BUNCH OF STACK INFORMATION
...
AttributeError: 'LP_c_double' object has no attribute '__array_interface__'

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

Ответы [ 5 ]

25 голосов
/ 04 декабря 2010

Создание массивов NumPy из объекта указателя ctypes является проблематичной операцией.Неясно, кому на самом деле принадлежит память, на которую указывает указатель.Когда он снова будет освобожден?Как долго это действует?По возможности я старался избегать такого рода конструкции.Гораздо проще и безопаснее создавать массивы в коде Python и передавать их в функцию C, чем использовать память, выделенную не зависящей от Python функцией C.Выполняя последнее, вы в некоторой степени сводите на нет преимущества наличия языка высокого уровня, обеспечивающего управление памятью.

Если вы действительно уверены, что кто-то заботится о памяти, вы можете создать объектразоблачение Python «буферный протокол», а затем создать массив NumPy, используя этот объект буфера.Вы дали один способ создания объекта буфера в своем посте через недокументированную функцию int_asbuffer():

buffer = numpy.core.multiarray.int_asbuffer(
    ctypes.addressof(y.contents), 8*array_length)

(Обратите внимание, что я заменил 8 на np.dtype(float).itemsize. Это всегда 8 на любой платформе.) Другой способ создания объекта буфера - вызвать функцию PyBuffer_FromMemory() из Python C API через ctypes:

buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
buffer = buffer_from_memory(y, 8*array_length)

Для обоих этих способов вы можете создать массив NumPy из buffer by

a = numpy.frombuffer(buffer, float)

(Я действительно не понимаю, почему вы используете .astype() вместо второго параметра для frombuffer; более того, мне интересно, почему вы используете np.int, в то время как вы ранее говорили, чтомассив содержит double с.)

Боюсь, это не станет намного легче, чем это, но это не так уж плохо, не правда ли?Вы можете похоронить все уродливые детали в функции-обертке и больше не беспокоиться об этом.

9 голосов
/ 21 ноября 2012

Еще одна возможность (для которой могут потребоваться более свежие версии библиотек, чем было доступно при написании первого ответа - я протестировал нечто подобное с ctypes 1.1.0 и numpy 1.5.0b2), - преобразовать указатель в массив.

np.ctypeslib.as_array(
    (ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents)))

Кажется, что у него все еще есть семантика общего владения, поэтому вам, вероятно, необходимо в конце концов освободить основной буфер.

8 голосов
/ 21 ноября 2015

Ни один из них не работал для меня в Python 3. Как общее решение для преобразования указателя ctypes в numpy ndarray в Python 2 и 3, я нашел, что это работает (через получение буфера только для чтения):

def make_nd_array(c_pointer, shape, dtype=np.float64, order='C', own_data=True):
    arr_size = np.prod(shape[:]) * np.dtype(dtype).itemsize 
    if sys.version_info.major >= 3:
        buf_from_mem = ctypes.pythonapi.PyMemoryView_FromMemory
        buf_from_mem.restype = ctypes.py_object
        buf_from_mem.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int)
        buffer = buf_from_mem(c_pointer, arr_size, 0x100)
    else:
        buf_from_mem = ctypes.pythonapi.PyBuffer_FromMemory
        buf_from_mem.restype = ctypes.py_object
        buffer = buf_from_mem(c_pointer, arr_size)
    arr = np.ndarray(tuple(shape[:]), dtype, buffer, order=order)
    if own_data and not arr.flags.owndata:
        return arr.copy()
    else:
        return arr
0 голосов
/ 20 ноября 2018

np.ctypeslib.as_array - это все, что вам нужно.

Из массива:

 c_arr = (c_float * 8)()
 np.ctypeslib.as_array(c_arr)

Из указателя

 c_arr = (c_float * 8)()
 ptr = ctypes.pointer(c_arr[0])
 np.ctypeslib.as_array(ptr, shape=(8,))
0 голосов
/ 13 августа 2016

Если вы согласны с созданием массивов в python, следующий пример с массивом 2d работает в python3:

import numpy as np
import ctypes

OutType = (ctypes.c_float * 4) * 6
out = OutType()
YourCfunction = ctypes.CDLL('./yourlib.so').voidreturningfunctionwithweirdname
YourCfunction.argtypes = [ctypes.POINTER(ctypes.c_float)]*3, ctypes.POINTER(ctypes.c_float)]*5, OutType]
YourCfunction(input1, input2, out)
out = np.array(out) # convert it to numpy

print(out)

numpy и ctypes версии 1.11.1 и 1.1.0

...