Bonjour,
Dans la liste donnée, je ne connais que Cython, et je le trouve très intéressant. Comme il y a longtemps que je n'ai pas programmé en C/C++, j'apprécie de ne pas avoir à le faire...
Un des avantages que j'y vois, c'est qu'en utilisant la solution "décorateur", on peut accélérer des fonctions Python sans modifier leur code.
Si on accepte de programmer en Python en visant Cython, on peut utiliser directement les fonctions de la bibliothèque C (la doc parle aussi de C++). Par exemple, pour utiliser la fonction exp dans une fonction compilée Cython, il suffira de la définir comme suit:
1 2
| cdef extern from "math.h":
double exp(double x) |
A noter tout de même que le gain en rapidité dépend de la "proximité" du codage Python par rapport au C. Par exemple, si on utilise à fond les structures de données Python qui n'existent pas en C (le "long" de Python par exemple), on oblige le code compilé à faire appel en permanence à l'API Python, ce qui fait qu'on ne gagne plus rien.
Prenons un exemple: une fonction Python qui calcule e à la puissance x en passant par les séries.
1- version en pur Python:
1 2 3 4 5 6 7 8
| def expo1(x, eps=1e-15):
"""calcul de e puissance x par les séries"""
n, t, s = 0, 1.0, 1.0
while t/s>eps:
n += 1
t = t*x/n
s = s + t
return s |
2- version en Cython avec la solution "décorateur" => dans un fichier séparé appelé ici "test01bib.pyx" :
1 2 3 4 5 6 7 8 9 10 11 12 13
| from cython import cclass, ccall, cfunc, returns as creturns, locals as clocals
#############################################################################
@ccall
@creturns(double)
@clocals(x=double, eps=double, n=int, t=double, s=double)
def expo2(x, eps=1e-15):
"""calcul de e puissance x par les séries"""
n, t, s = 0, 1.0, 1.0
while t/s>eps:
n += 1
t = t*x/n
s = s + t
return s |
le décorateur "ccall" indique le type de fonction. Ici, ce sera une fonction qu'on pourra appeler à partir du C et à partir du Python. Il y a d'autres types: "cclass" pour une classe et "cfunc" pour appel uniquement à partir du C.
Le reste est évident: on précise les types de données utilisées pour que le compilateur C sache quoi faire.
On compile avec (ce qui fabrique "test01bib.pxd"):
python setup.py build_ext --inplace
Avec un setup.py qui contient (NB: pas de caractères accentués, même dans les commentaires!):
1 2 3 4 5 6 7 8 9 10 11
| from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension("test01bib", ["test01bib.pyx"])]
setup(
name = 'bibliotheque',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
) |
Et on importe cette fonction compilée avec (elle sera extraite directement de "test01bib.pyd"):
from test01bib import expo2
Voilà. On peut maintenant faire des essais pour comparer la solution en pur Python avec la solution Cython: avec 1 million de valeurs au hasard, la solution Cython est 10 fois plus rapide que la solution Python!
Bonus: sous Windows, comme j'ai Visual C, Cython le prend par défaut. Quand je veux qu'il utilise plutôt mingw32, je place dans le même répertoire un fichier texte appelé "setup.cfg" contenant:
1 2
| [build]
compiler = mingw32 |
[Edit] J'ai dit plus haut qu'on pouvait utiliser directement les fonctions de bibliothèques C/C++. Cela veut dire qu'on peut créer facilement en Python compilé par Cython, une interface pour utiliser des bibliothèques C/C++ et les appeler directement à partir du code en pur Python. Par exemple:
1 2 3 4 5 6 7 8 9 10
| cdef extern from "math.h":
double exp(double x)
#############################################################################
@ccall # pour appel possible par du C ou par du Python
@creturns(double)
@clocals(x=double)
def exp2(x):
"""calcul de e puissance x par la bibliothèque C """
return exp(x) |
Une fois compilé par Cython, ce code permettra à un programme en pur Python, après une simple importation, d'appeler exp2 qui appellera directement le exp de la bibliothèque C.
Partager