Python et les décorateurs

Un décorateur permet d’encapsuler une focntion dans une autre fonction.

Cela peut permettre d’avoir un fonctionnement similaire à un héritage mais au niveau d’une fonction.

Un exemple simple

def decorate(fn):
        fn.__doc__ = 'fonction decoree %s' % fn.__doc__
        return fn

@decorate
def a_function():
        """ma fonction"""
        print "taitement"

print(a_function.__doc__)

retourne

fonction decoree ma fonction

On peut ainsi, par exemple, contrôler un argument sur un ensemble de fonction. dans cet exemple nous controlons que l’argument est un entier

def only_ints(fn):
        def _only_ints(arg):
                if not isinstance(arg,int):
                        raise TypeError("'%s' doit etre un entier" % str(arg))
                return fn(arg)
        return _only_ints

@only_ints
def b_function(arg):
        return arg + 1

print(b_function(1))
print(b_function("a"))

ce qui donne

2
Traceback (most recent call last):
  File "test_decoration.py", line 24, in <module>
    print(b_function("a"))
  File "test_decoration.py", line 15, in _only_ints
    raise TypeError("'%s' doit etre un entier" % str(arg))
TypeError: 'a' doit etre un entier

il est possible de décorer la même fonction avec plusieurs décorateurs. ainsi

def decorate1(fn):
        print('decorate1')
        return fn

def decorate2(fn):
        print('decorate2')
        return fn

@decorate1
@decorate2
def c_function():
        print "taitement"

print(c_function())

ce qui revient à

decorate1(decorate2(c_function))

ce qui donne

decorate1
decorate2
taitement

il est aussi possible d’imaginer une gestion de log qui contrôle le début et la fin du lancment de la fonction

def log(fn):
        def _log_fn(arg):
            print('%s start function' % fn.__name__)
            fn_exec = fn(arg)
            print('%s end function' % fn.__name__)
            return fn_exec
        return _log_fn

@log
def d_function(arg):
        print "taitement d"
        return arg

print(d_function(1))

ce qui donne

d_function start function
taitement d
d_function end function
1

un décorateur avec un paramètre

def decorateur(valeur):
        def fonction_interne(fonction):
                def fonction_retour():
                return fonction + valeur
            return fonction_retour
        return fonction_interne

@decorateur("coucou")
def lm():
        print "fred"

décorateur générique

def decorateur(fonction):
        def fonction_interne(*args, **kwargs):
                print "liste des arguments"
                print "args: %s" % (args,)
                print "kwargs: %s" % (kwargs,)
            return fonction(*args, **kwargs)
        return fonction_interne

@decorateur()
def lm(a,b):
        return a+b

Pour gérer des propriétés de classe

class Mamouth(object):

    _valeur = "3 calots"

    @property
    def valeur(self):
        return self._valeur.upper()

    @valeur.setter
    def valeur(self, valeur):
        self._valeur = valeur.strip()

>>> bille = Mamouth()
>>> bille.valeur
u'3 CALOTS'
>>> bille.valeur = "une pépite           "
>>> bille.valeur
>>> print(bille.valeur)
UNE PÉPITE