Obtenir des données du tableau ctypes dans numpy
j'utilise un Python (via ctypes
) enveloppé bibliothèque C pour exécuter une série de calcul. À différentes étapes de la course, je veux obtenir des données en Python, et spécifiquement numpy
tableaux.
l'enrubannage que j'utilise fait deux types différents de retour pour les données de tableau (qui est d'un intérêt particulier pour moi):
ctypes
Array: Quand je faistype(x)
(où x est lectypes
array, je reçois un<class 'module_name.wrapper_class_name.c_double_Array_12000'>
en retour. Je sais que ces données sont une copie des données internes de la documentation et je peux les mettre dans unnumpy
array facilement:>>> np.ctypeslib.as_array(x)
cela renvoie un 1D numpy
tableau de données.
ctype
pointeur de données: dans ce cas de la documentation de la bibliothèque, je comprends que je reçois un pointeur vers les données stockées et utilisées directement à la bibliothèque. La Whey je netype(y)
(où y est le pointeur)<class 'module_name.wrapper_class_name.LP_c_double'>
. Avec ce cas, je suis encore capable d'indexer à travers les données commey[0][2]
, mais je n'ai pu l'obtenir dans numpy par l'intermédiaire d'un super maladroit:>>> np.frombuffer(np.core.multiarray.int_asbuffer( ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize))
j'ai trouvé ça dans un vieux numpy
liste de diffusion fil de Travis Oliphant, mais pas dans le numpy
documentation. Si au lieu de cette approche j'essaie comme ci-dessus j'obtiens ce qui suit:
>>> np.ctypeslib.as_array(y)
...
... BUNCH OF STACK INFORMATION
...
AttributeError: 'LP_c_double' object has no attribute '__array_interface__'
Est-ce np.frombuffer
approche la meilleure ou la seule façon de le faire? Je suis ouvert aux autres suggestions mais doit toujours vouloir utiliser numpy
comme j'ai beaucoup d'autres codes post-traitement qui dépendent de numpy
fonctionnalités que je veux utiliser ces données.
4 réponses
créer des tableaux NumPy à partir d'un objet pointeur ctypes est une opération problématique. Il n'est pas clair qui possède réellement le souvenir que le pointeur pointe. Quand il sera libéré à nouveau? Combien de temps est-il valable? Dans la mesure du possible, j'essaierais d'éviter ce genre de construction. Il est tellement plus facile et plus sûr de créer des tableaux dans le code Python et de les passer à la fonction C que d'utiliser de la mémoire allouée par une fonction C ignorante de Python. En faisant ce dernier, vous niez dans une certaine mesure avantages d'avoir un langage de haut niveau en prenant soin de la gestion de la mémoire.
si vous êtes vraiment sûr que quelqu'un s'occupe de la mémoire, vous pouvez créer un objet exposant le "protocole tampon" de Python et ensuite créer un tableau NumPy en utilisant cet objet tampon. Vous avez donné une façon de créer l'objet buffer dans votre post, via le int_asbuffer()
fonction:
buffer = numpy.core.multiarray.int_asbuffer(
ctypes.addressof(y.contents), 8*array_length)
(notez que j'ai substitué 8
np.dtype(float).itemsize
. Il est toujours 8h, sur n'importe quelle plate-forme.) Un une autre façon de créer l'objet buffer serait d'appeler PyBuffer_FromMemory()
fonction de l'Python API C via ctypes:
buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
buffer = buffer_from_memory(y, 8*array_length)
Pour ces deux méthodes, vous pouvez créer un tableau NumPy de buffer
par
a = numpy.frombuffer(buffer, float)
(en fait, je ne comprends pas pourquoi vous utilisez .astype()
au lieu d'un deuxième paramètre frombuffer
; de plus, je me demande pourquoi vous utilisez np.int
, alors que vous avez dit plus tôt que le tableau contient double
s).
j'ai bien peur que ce ne soit pas beaucoup plus facile que cela, mais il n'est pas mauvais en soit, vous ne pensez pas? Vous pourriez enterrer tous les détails laids dans une fonction d'emballage et ne vous inquiétez plus à ce sujet.
une autre possibilité (qui peut nécessiter des versions plus récentes des bibliothèques que celles disponibles lorsque la première réponse a été écrite -- j'ai testé quelque chose de similaire avec ctypes 1.1.0
et numpy 1.5.0b2
) est de passer du pointeur au tableau.
np.ctypeslib.as_array(
(ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents)))
cela semble avoir toujours la sémantique de la propriété partagée, donc vous devez probablement vous assurer que vous libérez éventuellement le tampon sous-jacent.
aucun de ceux-ci n'a fonctionné pour moi en Python 3. Comme solution générale pour convertir un pointeur ctypes en un ndarray nomptueux en python 2 et 3, j'ai trouvé que cela fonctionnait (via l'obtention d'un tampon en lecture seule):
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
si vous êtes d'accord avec la création de tableaux en python, l'exemple suivant avec les tableaux 2d fonctionne 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)
les versions de numpy et ctypes sont 1.11.1 et 1.1.0