Décorateur en python

J'ai choisi les décorateurs pour commencer cette série sur la méta-programmation.

La méta-programmation en 2 mots, c'est le fait de modifier ou de générer le comportement d'un programme en runtime. Cela peut s'avérer très utile voir indispensable pour faire des programmes réutilisables pour plus détails.

En python la méta-programmation est plus évolué que dans pas mal de langages objet tel que le Java ou .net mais cela nuit beaucoup à ses performances, je n'aime pas trop parler benchmarking donc que je préfère plutôt me focaliser sur mon sujet.

Comme définition personnelle :) je dirai qu'un décorateur python est une fonction qui prend en paramètre une autre fonction, pour la modifier , lui ajouter des fonctionnalités, la substituer ou simplement exécuter un travail avant ou après l'avoir appellé. un décorateur est caractérisé par le mot clé @ placé au dessus de la fonction ciblé.

Exemple:

>>> def mon_decorateur(f):
... def _mon_decorateur():
... print "decorator stuff"
... f()
... print "other stuff"
... return
... return _mon_decorateur
...
>>> @mon_decorateur
... def decorate():
... print "fonction stuff"
...
>>> decorate()
decorator stuff
fonction stuff
other stuff

Comme vous pouvez le remarquer sur l'exemple, dès qu'on applique un décorateur sur une fonction c'est le décorateur qui prend le contrôle, autrement dit, il peut carement ignorer la function decorate().

Corrigez moi si je me trompe mais cela est possible en python et pas dans d'autres langages car le python interprète une fonction comme étant un objet. un objet dit de type callable ou implémentant la méthode call. pour passer des paramètres à un décorateur:

>>> def mon_decorateur(arg1):
... def _mon_decorateur(f):
... def __mon_decorateur():
... if arg1:
... return f()
... else:
... #do other stuff
... print "other stuff"
... return
... return __mon_decorateur
... return _mon_decorateur
...
>>> @mon_decorateur(True)
... def f1():
... print "called"
...
>>> @mon_decorateur(False)
... def f2():
... print "not called"
...
>>> f1()
called
>>> f2()
other stuff
>>> f1.__name__
'__mon_decorateur'
>>> f2.__name__
'__mon_decorateur'

Comme vous pouvez le remarquer la fonction f1 à carrément pris le nom de la fonction _mondécorateur ce qui veut dire que quand on appelle f1 c'est _mondécorateur qui est appelé à la place.

Pour montrer un peu l'utilité des décorateurs, on crée une fonction réutilisable qui met en cache le résultat de n'importe quelle fonction, comme ça si elle est appellé une 2ème fois avec les mêmes paramètres le résultat sera instantané. biensur cette fonction est basique (à ne pas utiliser en production), mais l'utilité ici est simplement voir un cas concret.

>>> def cache(function):
... list = {}
... def _cache(*args):
... key = "%s %s" % (function.__name__, args)
... try:
... res = list[key]
... print "function not called"
... except:
... print "function called"
... res = function(*args)
... list[key] = res
... return res
... return _cache
...
>>> @cache
... def f3(p1, p2):
... """very hard stuff ;)"""
... return "sum of %d + %d is %d" % (p1,p2,(p1+p2))
...
>>> f3(2,1)
function called
'sum of 2 + 1 is 3'
>>> f3(2,2)
function called
'sum of 2 + 2 is 4'
>>> f3(2,1)
function not called
'sum of 2 + 1 is 3'

Notre décorateur est maintenant applicabale sur n'importe quelle fonction avec ou sans args (mais pas sur les kwargs).

J'espère que cette petite intro vous sera utile, bon développement. Pour allez plus loin

Ajouter un commentaire

Nom :*
Email :*
Site perso :
Commentaire :*
chargement