Python prévoit de pouvoir appeler directement un code en C ou en C++ avec distutils. Le mécanisme nécessaire pour ça est assez logique mais assez complexe au niveau syntaxe. La présente page traite un exemple simple en C++.
Documentation pour Python 2.7: http://docs.python.org/2/extending/
C'est écrit pour Python 2.7, mais la conversion pour Python 3.3 ne devrait pas poser de problème.
Documentation pour Python 3.3: http://docs.python.org/3/extending/
On va créer un module contenant 2 fonctions:
Ecrits en code C++ simple, voici les codes, placés dans un même fichier: testext.cpp:
// fonction 'fact' en C++ int fact(int n) { if (n < 2) return(1); return (n)*fact(n-1); }; // fonction 'pgcd' en C++ int pgcd(int a, int b) { int r; while (b!=0) { r = a%b; a = b; b = r; } return a; };
Il va falloir maintenant habiller ces 2 codes pour que l'interpréteur Python soit capable d'importer le module et d'utiliser correctement ces 2 fonctions. Cela va se faire en 4 étapes:
1- inclure l'en-tête Python.h (Sous Windows, elle est livrée avec Python, et se trouve dans C:\Python27\include)
#include "Python.h"
2- placer un code d'initialisation du module, qui sera exécuté par Python lors de l'importation:
PyMODINIT_FUNC inittestext(void) { Py_InitModule("testext", testextMethods); };
Dans ce code, testext est le nom du module, et testextMethods l'objet qui va décrire tous les objets disponibles du module (voir point suivant)
3- placer le code qui va donner les objets disponibles du module:
static PyMethodDef testextMethods[] = { {"fact", testext_fact, METH_VARARGS}, {"pgcd", testext_pgcd, METH_VARARGS}, {NULL, NULL}, };
4- enfin, chaque fonction C++ va être “encapsulée” par les codes suivants:
Pour la fonction “fact”:
// encapsulation de la fonction 'fact' pour utilisation par Python static PyObject * testext_fact(PyObject *self, PyObject *args) { int num; if (!PyArg_ParseTuple(args, "i", &num)) return NULL; return (PyObject*)Py_BuildValue("i", fact(num)); };
Et pour la fonction “pgcd”:
// encapsulation de la fonction 'pgcd' pour utilisation par Python static PyObject * testext_pgcd(PyObject *self, PyObject *args) { int a, b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL; // cas où a ou b n'est pas un int return (PyObject*)Py_BuildValue("i", pgcd(a,b)); };
Et c'est tout! Voilà le code complet du module “testext.cpp”
// creation d'un module en C++ pour importation dans Python #include "Python.h" //########################################################################### // fonction 'fact' en C++ int fact(int n) { if (n < 2) return(1); return (n)*fact(n-1); }; //=========================================================================== // encapsulation de la fonction 'fact' pour utilisation par Python static PyObject * testext_fact(PyObject *self, PyObject *args) { int num; if (!PyArg_ParseTuple(args, "i", &num)) return NULL; return (PyObject*)Py_BuildValue("i", fact(num)); }; //########################################################################### // fonction 'pgcd' en C++ int pgcd(int a, int b) { int r; while (b!=0) { r = a%b; a = b; b = r; } return a; }; //=========================================================================== // encapsulation de la fonction 'pgcd' pour utilisation par Python static PyObject * testext_pgcd(PyObject *self, PyObject *args) { int a, b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL; // cas où a ou b n'est pas un int return (PyObject*)Py_BuildValue("i", pgcd(a,b)); }; //########################################################################### // liste des objets disponibles du module static PyMethodDef testextMethods[] = { {"fact", testext_fact, METH_VARARGS}, {"pgcd", testext_pgcd, METH_VARARGS}, {NULL, NULL}, }; //########################################################################### // initialisation du module par Python pendant l'importation PyMODINIT_FUNC inittestext(void) { Py_InitModule("testext", testextMethods); };
Malgré tout, ce ne sont que des exemples simples et purement numériques. Pour des codes plus complexes, il faudra faire un peu attention, en particulier au points suivants:
Voilà le “setup.py” nécessaire à la compilation:
#!/usr/bin/python # -*- coding: utf-8 -*- # Python 2.7 from distutils.core import setup, Extension ext_modules = [Extension('testext', sources=['testext.cpp'], include_dirs=["."], language="c++" ) ] setup(name='testext', ext_modules=ext_modules )
Vous voyez que le choix du “C++” est signalé dans le setup
La compilation en C++ est obtenue par (en console et dans le répertoire du setup.py):
python setup.py build
Attention sous Windows: choix du compilateur
Python prendra le compilateur par défaut. Si vous avez Visual Studio de Microsoft (comme moi), c'est lui qui sera utilisé (ça fonctionne très bien!). Mais si vous voulez MinGW au lieu de Visual Studio, il y a 2 choses à faire:
1- créer un fichier texte “setup.cfg” placé au même niveau que setup.py, et contenant:
[build] compiler = mingw32
2- corriger le fichier C:\Python27\Lib\distutils\cygwinccompiler.py pour retirer toute mention de l'option “-mno-cygwin” si elle génère une erreur.
Moyennant quoi, chez moi, ça marche avec les 2 compilateurs.
Avec Windows, il se crée un module “testext.pyd” dans le répertoire build\lib.win32-2.7, et ce module pourra être directement importé par Python pour être utilisé.
Voilà un petit code d'essai:
#!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import division # Python 2.7 from testext import fact, pgcd from timeit import default_timer from random import randint n = 1000000 a = randint(10,10000) b = randint(10,10000) t1 = default_timer() for c in xrange(0, n): pgcd(a, b) t1 = default_timer()-t1 print t1 print fact(10)
Exemple d'affichage:
0.382636144736 3628800
A noter que le temps obtenu est un peu moins bon qu'avec Cython! En fait, ce n'est pas le code C++ lui-même qui fait la différence, mais la “machinerie” que Python fabrique autour de ce code pour permettre l'utilisation par l'interpréteur Python. On peut supposer que des fonctions en C++ plus complexes et plus longues donneront moins d'importance à cette “machinerie” et permettront un plus grand avantage de temps d'exécution.
Amusez-vous bien!