Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Industrial programming >> Python

Maîtriser la programmation d'extensions Python avec C : Construire des modules natifs rapides

Tout code écrit en langage compilé (C, C++, Java) peut être intégré ou importé dans un script Python. Ce code est considéré comme une extension.

Un module d'extension Python n'est rien d'autre qu'une bibliothèque C normale. Sur Unix, ces bibliothèques se terminent généralement par .so (shared object). Sur Windows, on voit typiquement .dll (dynamically linked library).

Prérequis pour l'écriture d'extensions

Pour commencer, vous avez besoin des fichiers d'en-tête Python.

De plus, une bonne connaissance du C ou C++ est nécessaire pour écrire des extensions Python en C.

Premier regard sur une extension Python

Pour votre première extension, regroupez votre code en quatre parties :

Le fichier d'en-tête Python.h

Incluez Python.h dans votre fichier source C pour accéder à l'API interne Python et brancher votre module dans l'interpréteur.

Incluez Python.h avant tout autre en-tête. Suivez avec les fonctions à appeler depuis Python.

Les fonctions C

Les signatures des implémentations C de vos fonctions prennent toujours l'une de ces trois formes :

static PyObject *MyFunction(PyObject *self, PyObject *args);
static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw);
static PyObject *MyFunctionWithNoArgs(PyObject *self);

Chaque déclaration retourne un objet Python. Il n'y a pas de fonction void en Python comme en C. Si vous ne voulez pas retourner de valeur, retournez l'équivalent C de None de Python. Les en-têtes Python définissent la macro Py_RETURN_NONE pour cela.

Les noms de vos fonctions C peuvent être ce que vous voulez car ils ne sont jamais vus hors du module d'extension. Ils sont définis comme static.

Vos fonctions C sont généralement nommées en combinant le nom du module et de la fonction Python :

static PyObject *module_func(PyObject *self, PyObject *args) {
    /* Faites votre travail ici. */
    Py_RETURN_NONE;
}

C'est une fonction Python appelée func dans le module module. Vous placerez des pointeurs vers vos fonctions C dans la table des méthodes du module.

La table de correspondance des méthodes

Cette table est un tableau de structures PyMethodDef :

struct PyMethodDef {
    char *ml_name;
    PyCFunction ml_meth;
    int ml_flags;
    char *ml_doc;
};

Description des membres :

La table doit se terminer par une sentinelle de valeurs NULL et 0.

Exemple

Pour la fonction ci-dessus, table de correspondance :

static PyMethodDef module_methods[] = {
    {"func", (PyCFunction)module_func, METH_NOARGS, NULL},
    {NULL, NULL, 0, NULL}
};

La fonction d'initialisation

Dernière partie du module d'extension : la fonction d'initialisation. Appelée par l'interpréteur Python au chargement du module. Doit s'appeler initModule, où Module est le nom du module.

La fonction doit être exportée depuis la bibliothèque. Les en-têtes Python définissent PyMODINIT_FUNC pour les incantations appropriées selon l'environnement de compilation.

Structure générale :

PyMODINIT_FUNC initModule() {
    Py_InitModule3("func", module_methods, "docstring...");
}

Description de Py_InitModule3 :

Ensemble complet :

#include 
static PyObject *module_func(PyObject *self, PyObject *args) {
    /* Do your stuff here. */
    Py_RETURN_NONE;
}
static PyMethodDef module_methods[] = {
    {"func", (PyCFunction)module_func, METH_NOARGS, NULL},
    {NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initModule() {
    Py_InitModule3("func", module_methods, "docstring...");
}

Exemple complet

#include 
static PyObject* helloworld(PyObject* self) {
    return Py_BuildValue("s", "Hello, Python extensions!!");
}
static char helloworld_docs[] = "helloworld(): Any message you want to put here!!\n";
static PyMethodDef helloworld_funcs[] = {
    {"helloworld", (PyCFunction)helloworld, METH_NOARGS, helloworld_docs},
    {NULL}
};
void inithelloworld(void) {
    Py_InitModule3("helloworld", helloworld_funcs, "Extension module example!");
}

La fonction Py_BuildValue construit une valeur Python. Sauvegardez dans hello.c. Voyons comment compiler et installer pour l'appeler depuis Python.

Construction et installation d'extensions

Le paquet distutils facilite la distribution de modules Python (purs ou extensions) de façon standard. Les modules sont distribués en source et construits/installés via un script setup.py.

Pour le module ci-dessus, setup.py :

from distutils.core import setup, Extension
setup(name='helloworld', version='1.0',
      ext_modules=[Extension('helloworld', ['hello.c'])]) 

Commande pour compiler, lier et copier la bibliothèque dynamique :

$ python setup.py install

Sur Unix, lancez en root pour écrire dans site-packages. Pas de problème sur Windows.

Importation des extensions

Une fois installé, importez et appelez l'extension dans votre script Python :

#!/usr/bin/python
import helloworld
print(helloworld.helloworld())

Résultat :

Hello, Python extensions!!

Passage de paramètres aux fonctions

Pour définir des fonctions acceptant des arguments, utilisez l'une des autres signatures. Exemple :

static PyObject *module_func(PyObject *self, PyObject *args) {
    /* Parse args et faites quelque chose d'intéressant. */
    Py_RETURN_NONE;
}

Table des méthodes avec entrée pour la nouvelle fonction :

static PyMethodDef module_methods[] = {
    {"func", (PyCFunction)module_func, METH_NOARGS, NULL},
    {"func", module_func, METH_VARARGS, NULL},
    {NULL, NULL, 0, NULL}
};

Utilisez l'API PyArg_ParseTuple pour extraire les arguments du pointeur PyObject passé à votre fonction C.

Le premier argument de PyArg_ParseTuple est args. Le second est une chaîne de format décrivant les arguments attendus. Chaque argument est représenté par un ou plusieurs caractères :

static PyObject *module_func(PyObject *self, PyObject *args) {
    int i;
    double d;
    char *s;
    if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
        return NULL;
    }
    /* Faites quelque chose d'intéressant ici. */
    Py_RETURN_NONE;
}

Compiler la nouvelle version et l'importer permet d'appeler la fonction avec divers arguments :

module.func(1, s="three", d=2.0)
module.func(i=1, d=2.0, s="three")
module.func(s="three", d=2.0, i=1)

La fonction PyArg_ParseTuple

Signature standard :

int PyArg_ParseTuple(PyObject* tuple, char* format, ...)

Retourne 0 pour erreur, valeur non-nulle pour succès. tuple est le PyObject* second argument. format est une chaîne C décrivant arguments obligatoires et optionnels.

Codes de format pour PyArg_ParseTuple :

CodeType CSignification
ccharChaîne Python longueur 1 → C char.
ddoubleFloat Python → C double.
ffloatFloat Python → C float.
iintInt Python → C int.
llongInt Python → C long.
Llong longInt Python → C long long.
OPyObject*Référence empruntée non-NULL vers argument Python.
schar*Chaîne Python sans nulls embarqués → C char*.
s#char*+intToute chaîne Python → adresse C et longueur.
t#char*+intTampon lecture-seule segment unique → adresse C et longueur.
uPy_UNICODE*Unicode Python sans nulls embarqués → C.
u#Py_UNICODE*+intTout Unicode Python → adresse C et longueur.
w#char*+intTampon lecture/écriture segment unique → adresse C et longueur.
zchar*Comme s, accepte aussi None (met C char* à NULL).
z#char*+intComme s#, accepte aussi None.
(...)selon ...Séquence Python traitée comme un argument par élément.
|Arguments suivants optionnels.
:Fin format, suivi du nom de fonction pour messages d'erreur.
;Fin format, suivi du texte complet du message d'erreur.

Retour de valeurs

Py_BuildValue prend une chaîne de format comme PyArg_ParseTuple. Au lieu d'adresses, on passe les valeurs réelles. Exemple d'implémentation d'une fonction add :

static PyObject *foo_add(PyObject *self, PyObject *args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
        return NULL;
    }
    return Py_BuildValue("i", a + b);
}

Équivalent Python :

def add(a, b):
    return (a + b)

Retourner deux valeurs (capturé comme liste en Python) :

static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
        return NULL;
    }
    return Py_BuildValue("ii", a + b, a - b);
}

Équivalent Python :

def add_subtract(a, b):
    return (a + b, a - b)

La fonction Py_BuildValue

Signature standard :

PyObject* Py_BuildValue(char* format, ...)

format décrit l'objet Python à construire. Les arguments suivants sont des valeurs C. Le résultat PyObject* est une nouvelle référence.

Codes couramment utilisés :

CodeType CSignification
ccharC char → chaîne Python longueur 1.
ddoubleC double → float Python.
ffloatC float → float Python.
iintC int → int Python.
llongC long → int Python.
NPyObject*Passe objet Python et vole une référence.
OPyObject*Passe objet Python et INCREF normalement.
O&convert+void*Conversion arbitraire.
schar*C char* terminé par null → chaîne Python, ou NULL → None.
s#char*+intC char* et longueur → chaîne Python, ou NULL → None.
uPy_UNICODE*Chaîne large C terminée par null → Unicode Python, ou NULL → None.
u#Py_UNICODE*+intChaîne large C et longueur → Unicode Python, ou NULL → None.
w#char*+intTampon lecture/écriture segment unique → adresse C et longueur.
zchar*Comme s, accepte None (met C char* à NULL).
z#char*+intComme s#, accepte None.
(...)selon ...Construit tuple Python depuis valeurs C.
[...]selon ...Construit liste Python depuis valeurs C.
{...}selon ...Construit dictionnaire Python depuis valeurs C, clés et valeurs alternées.

{...} construit des dictionnaires depuis un nombre pair de valeurs C, alternant clés et valeurs. Exemple : Py_BuildValue("{issi}", 23, "zig", "zag", 42) renvoie un dictionnaire comme {23: 'zig', 'zag': 42} en Python.

Python

  1. Maîtriser la programmation orientée objet Python : classes, héritage, encapsulation et polymorphisme
  2. Fonction Python print() : Guide pratique avec exemples
  3. Maîtriser la méthode strip() de Python : Guide complet et exemples pratiques
  4. Maîtriser str.count() de Python : Compter caractères et sous-chaînes avec exemples
  5. Maîtriser les chaînes format() en Python avec exemples clairs
  6. Maîtriser la méthode String.find() de Python : Syntaxe, Exemples et Alternatives
  7. Maîtriser les fonctions lambda Python : exemples pratiques et bonnes pratiques
  8. Fonction Python round() expliquée avec exemples pratiques
  9. Maîtriser la fonction Python map() : syntaxe, exemples et bonnes pratiques