python numpy ctypes

python - Obteniendo datos de ctypes array en numpy



(4)

Crear matrices NumPy desde un objeto puntero ctypes es una operación problemática. No está claro quién posee realmente la memoria a la que apunta el puntero. ¿Cuándo será liberado de nuevo? ¿Cuánto tiempo es válido? Siempre que sea posible, trataré de evitar este tipo de construcción. Es mucho más fácil y más seguro crear matrices en el código Python y pasarlas a la función C que utilizar la memoria asignada por una función C que no tenga conocimiento de Python. Al hacer esto último, niega en cierta medida las ventajas de tener un lenguaje de alto nivel que se ocupa de la administración de la memoria.

Si está realmente seguro de que alguien se ocupa de la memoria, puede crear un objeto que exponga el "protocolo de búfer" de Python y luego crear una matriz NumPy utilizando este objeto de búfer. Dio una forma de crear el objeto de memoria intermedia en su publicación, a través de la función int_asbuffer() no documentada:

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

(Tenga en cuenta que he sustituido 8 por np.dtype(float).itemsize . Siempre es 8 en cualquier plataforma). Una forma diferente de crear el objeto buffer sería llamar a la función PyBuffer_FromMemory() desde Python C API mediante ctypes:

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

Para ambas formas, puede crear una matriz NumPy desde el buffer

a = numpy.frombuffer(buffer, float)

(En realidad, no entiendo por qué usas .astype() lugar de un segundo parámetro para frombuffer , además, me pregunto por qué usas np.int , mientras que antes dijiste que el array contiene double s).

Me temo que no será mucho más fácil que esto, pero no es tan malo, ¿no crees? Podrías enterrar todos los detalles desagradables en una función de envoltura y no te preocupes más por eso.

Estoy usando una biblioteca C envuelta Python (via ctypes ) para ejecutar una serie de cálculos. En diferentes etapas de la ejecución, quiero obtener datos en Python, y específicamente numpy arrays.

La envoltura que estoy usando tiene dos tipos diferentes de retorno para los datos de la matriz (lo cual es de particular interés para mí):

  • ctypes Array : Cuando ctypes type(x) (donde x es la matriz ctypes , obtengo una <class ''module_name.wrapper_class_name.c_double_Array_12000''> a cambio. Sé que estos datos son una copia de los datos internos de la documentación y Puedo ponerlo en una matriz numpy fácilmente:

    >>> np.ctypeslib.as_array(x)

Esto devuelve una matriz 1D numpy de los datos.

  • puntero ctype a los datos : en este caso, de la documentación de la biblioteca, entiendo que estoy obteniendo un puntero a los datos almacenados y utilizados directamente en la biblioteca. Whey <class ''module_name.wrapper_class_name.LP_c_double''> type(y) (donde y es el puntero) Obtengo <class ''module_name.wrapper_class_name.LP_c_double''> . Con este caso todavía puedo indexar los datos como y[0][2] , pero solo pude obtener un numpy a través de un texto súper incómodo:

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

Encontré esto en un viejo hilo de la lista de correos numpy de Travis Oliphant , pero no en la documentación numpy . Si en lugar de este enfoque, intento lo de arriba, obtengo lo siguiente:

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

¿Es este enfoque np.frombuffer la mejor o la única forma de hacerlo? Estoy abierto a otras sugerencias, pero aún así me gustaría usar numpy ya que tengo muchos otros códigos de post-procesamiento que dependen de la funcionalidad numpy que quiero usar con estos datos .


Ninguno de estos me funcionó en Python 3. Como solución general para convertir un puntero ctypes en nudy ndarray en python 2 y 3 encontré que funcionaba (al obtener un buffer de solo lectura):

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


Otra posibilidad (que puede requerir versiones más recientes de las bibliotecas que está disponible cuando se escribió la primera respuesta, probé algo similar con ctypes 1.1.0 y numpy 1.5.0b2 ) es convertir del puntero a la matriz.

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

Parece que todavía tiene la semántica de propiedad compartida, por lo que probablemente deba asegurarse de liberar el búfer subyacente con el tiempo.


Si está bien con la creación de matrices en python, el siguiente ejemplo con 2d array funciona en 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)

las versiones numpy y ctypes son 1.11.1 y 1.1.0