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 fais type(x) (où x est le ctypes 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 un numpy 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 ne type(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 comme y[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.

24
demandé sur dtlussier 2010-12-04 22:58:01

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é 8np.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 doubles).

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.

23
répondu Sven Marnach 2010-12-04 20:36:45

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.

7
répondu seeker 2012-11-20 20:36:27

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
7
répondu wordy 2015-11-20 22:35:32

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

0
répondu Ilya Prokin 2016-08-12 22:28:15