ctypes - Débutant

100

J'ai la tâche de "envelopper" la bibliothèque ac dans une classe python. Les documents sont incroyablement vagues à ce sujet. Il semble qu'ils s'attendent à ce que seuls les utilisateurs avancés de python implémentent des ctypes. Eh bien, je suis un débutant en python et j'ai besoin d'aide.

Une aide étape par étape serait merveilleuse.

J'ai donc ma bibliothèque c. Que fais-je? Quels fichiers dois-je mettre où? Comment importer la bibliothèque? J'ai lu qu'il pourrait y avoir un moyen de "envelopper automatiquement" en Python?

(Au fait, j'ai fait le didacticiel ctypes sur python.net et cela ne fonctionne pas. Ce qui signifie que je pense qu'ils supposent que je devrais être en mesure de remplir le reste des étapes.

En fait, c'est l'erreur que j'obtiens avec leur code:

File "importtest.py", line 1
   >>> from ctypes import *
   SyntaxError: invalid syntax

Je pourrais vraiment utiliser une aide étape par étape à ce sujet! Merci ~

passéak
la source
10
Avez-vous le >>> dans importtest.py? Lorsque les gens publient le code qui a >>> sur chaque ligne, cela signifie qu'il est exécuté dans le shell interactif. Pour l'exécuter à partir d'un fichier, supprimez >>> (c'est 3> signes et un espace) partout où il apparaît.
Chinmay Kanchi
4
Ne tapez pas le >>>s. Ceux-ci sont imprimés par le shell interactif et doivent être laissés en dehors de votre fichier source.
nmichaels
8
>>>dans le fichier .py! AIE! Jamais vu ça avant!
David Heffernan
3
Honnêtement, apprenez un peu de Python (au moins un peu) avant de commencer à jouer avec les ctypes. Vous n'allez jamais trouver un tutoriel sur les ctypes qui suppose que vous ne connaissez pas Python de base.
Chinmay Kanchi
3
@spentak: si vous demandez de l'aide, fournissez des informations adéquates. Au moins, montrez-nous la dernière version du code dont vous parlez. Qu'y a-t-il sur "ligne 3", par exemple?
Francesco

Réponses:

229

Voici un tutoriel ctypes rapide et sale.

Tout d'abord, écrivez votre bibliothèque C. Voici un exemple simple de Hello world:

testlib.c

#include <stdio.h>

void myprint(void);

void myprint()
{
    printf("hello world\n");
}

Maintenant, compilez-le en tant que bibliothèque partagée ( correctif mac trouvé ici ):

$ gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC testlib.c

# or... for Mac OS X 
$ gcc -shared -Wl,-install_name,testlib.so -o testlib.so -fPIC testlib.c

Ensuite, écrivez un wrapper à l'aide de ctypes:

testlibwrapper.py

import ctypes

testlib = ctypes.CDLL('/full/path/to/testlib.so')
testlib.myprint()

Maintenant, exécutez-le:

$ python testlibwrapper.py

Et vous devriez voir la sortie

Hello world
$

Si vous avez déjà une bibliothèque en tête, vous pouvez ignorer la partie non-python du didacticiel. Assurez-vous que ctypes peut trouver la bibliothèque en la plaçant dans /usr/libun autre répertoire standard. Si vous faites cela, vous n'avez pas besoin de spécifier le chemin complet lors de l'écriture du wrapper. Si vous choisissez de ne pas le faire, vous devez fournir le chemin complet de la bibliothèque lors de l'appel ctypes.CDLL().

Ce n'est pas le lieu pour un tutoriel plus complet, mais si vous demandez de l'aide pour des problèmes spécifiques sur ce site, je suis sûr que la communauté vous aidera.

PS: Je suppose que vous êtes sous Linux parce que vous avez utilisé ctypes.CDLL('libc.so.6'). Si vous êtes sur un autre système d'exploitation, les choses peuvent changer un peu (ou beaucoup).

Chinmay Kanchi
la source
1
@ Chinmay: Puis-je avoir un code similaire pour Windows et au lieu de C, pourriez-vous s'il vous plaît fournir un exemple visuel C ++? Je peux charger ma bibliothèque mais je ne peux pas accéder à mes fonctions à partir du fichier .dll. Il dit toujours "fonction 'xyz' non trouvée". Pouvez-vous me suggérer un moyen de contourner ce problème? À votre santé.
Neophile
Je ne sais pas grand-chose sur le développement Windows, mais il semble que Windows fasse quelque chose de bancal, peut-être utilise-t-il une convention d'appel différente? Peut-être pourriez-vous chercher à exporter vos fonctions C ++ en utilisant "extern C"?
Chinmay Kanchi
Oui, je l'ai fait mais pas de chance pour l'instant.
Neophile le
6
Merci pour le tutoriel facile à suivre qui montre les fonctionnalités de base de ctype
okysabeni
1
rapide et sale sont toujours les meilleurs tutoriels
lurscher
55

La réponse de Chinmay Kanchi est excellente mais je voulais un exemple de fonction qui passe et retourne des variables / tableaux à un code C ++. J'ai pensé l'inclure ici au cas où cela serait utile à d'autres.

Passer et renvoyer un entier

Le code C ++ d'une fonction qui prend un entier et en ajoute un à la valeur retournée,

extern "C" int add_one(int i)
{
    return i+1;
}

Enregistré en tant que fichier test.cpp, notez l' externe "C" requis (il peut être supprimé pour le code C). Ceci est compilé en utilisant g ++, avec des arguments similaires à la réponse de Chinmay Kanchi,

g++ -shared -o testlib.so -fPIC test.cpp

Le code Python utilise à load_librarypartir de l' numpy.ctypeslibhypothèse du chemin d'accès à la bibliothèque partagée dans le même répertoire que le script Python,

import numpy.ctypeslib as ctl
import ctypes

libname = 'testlib.so'
libdir = './'
lib=ctl.load_library(libname, libdir)

py_add_one = lib.add_one
py_add_one.argtypes = [ctypes.c_int]
value = 5
results = py_add_one(value)
print(results)

Cela imprime 6 comme prévu.

Passer et imprimer une matrice

Vous pouvez également passer des tableaux comme suit, pour qu'un code C imprime l'élément d'un tableau,

extern "C" void print_array(double* array, int N)
{
    for (int i=0; i<N; i++) 
        cout << i << " " << array[i] << endl;
}

qui est compilé comme avant et importé de la même manière. Le code Python supplémentaire pour utiliser cette fonction serait alors,

import numpy as np

py_print_array = lib.print_array
py_print_array.argtypes = [ctl.ndpointer(np.float64, 
                                         flags='aligned, c_contiguous'), 
                           ctypes.c_int]
A = np.array([1.4,2.6,3.0], dtype=np.float64)
py_print_array(A, 3)

où nous spécifions le tableau, le premier argument vers print_array, comme un pointeur vers un tableau Numpy de flottants alignés, c_contiguous 64 bits et le deuxième argument comme un entier qui indique au code C le nombre d'éléments dans le tableau Numpy. Ceci est ensuite imprimé par le code C comme suit,

1.4
2.6
3.0
Ed Smith
la source
5
C'est une excellente réponse complémentaire - dommage qu'il ne puisse y avoir deux réponses cochées :(
jtlz2
Je ne sais pas si c'est trop évident, mais il y a une erreur dans le code. Il manque import numpy as np. Sinon, il ne peut pas trouver np.float64et les autres choses.
Ben
11

Premièrement: le >>>code que vous voyez dans les exemples python est un moyen d'indiquer qu'il s'agit de code Python. Il est utilisé pour séparer le code Python de la sortie. Comme ça:

>>> 4+5
9

Ici, nous voyons que la ligne qui commence par >>>est le code Python, et 9 est ce qu'il en résulte. C'est exactement à quoi ça ressemble si vous démarrez un interpréteur Python, c'est pourquoi c'est fait comme ça.

Vous n'entrez jamais la >>>pièce dans un .pyfichier.

Cela règle votre erreur de syntaxe.

Deuxièmement, ctypes n'est qu'une des nombreuses façons d'encapsuler les bibliothèques Python. D' autres moyens sont SWIG , qui se penchera sur votre bibliothèque Python et générer un module d'extension C Python qui expose l'API C. Une autre façon est d'utiliser Cython .

Ils ont tous des avantages et des inconvénients.

SWIG exposera uniquement votre API C à Python. Cela signifie que vous n'obtenez aucun objet ou quoi que ce soit, vous devrez créer un fichier Python séparé pour cela. Il est cependant courant d'avoir un module appelé say "wowza" et un module SWIG appelé "_wowza" qui est le wrapper autour de l'API C. C'est une manière simple et agréable de faire les choses.

Cython génère un fichier C-Extension. Cela présente l'avantage que tout le code Python que vous écrivez est transformé en C, de sorte que les objets que vous écrivez sont également en C, ce qui peut améliorer les performances. Mais vous devrez apprendre comment il s'interface avec C donc c'est un peu plus de travail pour apprendre à l'utiliser.

Les ctypes ont l'avantage qu'il n'y a pas de code C à compiler, il est donc très agréable à utiliser pour envelopper les bibliothèques standard écrites par quelqu'un d'autre, et existe déjà dans les versions binaires pour Windows et OS X.

Lennart Regebro
la source