IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Les décorateurs, à quoi cela sert-il ?


Sujet :

Python

  1. #1
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut Les décorateurs, à quoi cela sert-il ?
    Bonjour,
    tout est dans le titre.

    Toute info. est la bienvenue.

  2. #2
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Il en est question des décorateurs dans le hors-série de Linux Magazine consacré à Python (page 62).
    Je ne comprends pas tout. Voici ce qui en est dit:


    «Les décorateurs permettent de factoriser des comportements transverses, tels que de la vérification de type, de la journalisation d'appel, du profiling ou encore, comme dans l'exemple ci-après, du cache de valeurs.

    Un décorateur est une fonction qui va être appelée avec la fonction qu'on lui demande de décorer en paramètre, et qui retourne la fonction décorée.»


    Suit une description succinte de la syntaxe.
    Puis un exemple: décorateur cache
    :


    «Dans l'exemple suivant, le décorateur cache mémorise le résultat d'une fonction en rapport avec les paramètre passés:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    from functools import wraps
     
    def cache(f):
        f.cache={}
        @wraps(f)
        def cache_func(*args):
            if args in f.cache():
                return f.cache[args]
            f.cache[args] = res = f(*args)
            return res
        return cache_func
     
    @cache
    def factorielle(n):
        print "calcul de %i!" % n
        return n * factorielle(n-1) if n > 0 else 1
    Son fonctionnement est de créer une nouvelle fonction qui va envelopper la fonction à décorer. Ainsi, ici, factorielle est remplacée par une fonction cache_func créée expressément pour lui ajouter un comportement de gestion cache.

    Puisque factorielle est maintenant cache_func, si on ignore qu'elle a été décorée, on peut être surpris par la valeur de ces attributs, à commencer par factorielle.__func_name qui devrait retourner cache_func au lieu de factorielle. Pour éviter cela, nous avons décoré cache_func avec le décorateur wraps(f). wraps est une fonction qui retourne un décorateur. Pour être plus précis, elle retourne un décorateur qui maquille la fonction décorée pour qu'elle reflète les attributs de la fonction passée en paramètres de wraps.

    Ce qui donne à l'utilisation:

    >>> factorielle(3)
    calcul de 3!
    calcul de 2!
    calcul de 1!
    calcul de 0!
    6
    >>> factorielle(5)
    calcul de 5!
    calcul de 4!
    120
    >>>factorielle.cache
    {(0,):1,(1,):1,(2,):2,(3,):6,(4,):24,(5,):120}
    Nous aurions pu intégrer le mécanisme de cache directement à la fonction factorielle, mais ce comportement n'aurait alors pas été réutilisable, pour une fonction fibonacci par exemple.
    Les décorateurs permettent d'extraire ce genre de comportements transverses et de les factoriser.
    De plus, le but de la fonction factorielle est de calculer une factorielle, pas de gérer un cache. Mélanger les deux problèmes dans la même fonction aurait complexifié le code, alors que, ici, on traite séparément chaque problème. Les codes en sont plus lisibles.

    Ce sont des concepts que l'on retrouve en programmation orientée aspect.»


    Je conseille d'acheter ce numéro hors-série.
    Linux Magazine/France
    Hors-série No 40.
    Janvier-Février 2009.
    72 pages sans pub.

    Bien qu'on aimerait que certains articles soient plus détaillés, il est quand même très dense et il donne un bonne vision globale du panorama Python.



    J'avoue que je n'ai pas tout compris dans l'exemple donné. J'ai essayé de le faire tourner mais j'ai l'erreur:
    File "C:/Python/Essais Python/essai decorateur.py", line 7, in cache_func
    if args in f.cache():
    TypeError: 'dict' object is not callable

    Est-ce parce que j'utilise Python 2.5.1 ?
    L'article ne précise pas pour quelle version de Python ses exemples sont valables.

  3. #3
    Membre émérite
    Avatar de DelphiManiac
    Homme Profil pro
    Homme à tout faire
    Inscrit en
    Mars 2002
    Messages
    1 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Homme à tout faire
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 147
    Points : 2 535
    Points
    2 535
    Par défaut
    Citation Envoyé par eyquem Voir le message
    ...J'avoue que je n'ai pas tout compris dans l'exemple donné. J'ai essayé de le faire tourner mais j'ai l'erreur:
    File "C:/Python/Essais Python/essai decorateur.py", line 7, in cache_func
    if args in f.cache():
    TypeError: 'dict' object is not callable

    ...
    L'errreur est sur la ligne et n'est pas lié au décorateur, c'est juste une erreur de syntaxe basique. qui doit être Le principe d'un décorateur est d'avoir un fonction qui est appellé avant la 'vrai' fonction.

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    def decorate(f):
        print u'lors de l\'initialisation du décorateur de la fonction %s' % f.__name__
        my_fonction_name = f.__name__
        def wrapper(*args):
            print u'avant l\'appel de la fonction %s avec les arguments suivant' % my_fonction_name
            for arg in args:
                print arg
            res = f(*args)
            print u'après l\'appel de la fonction %s ' % my_fonction_name
            print
            return res
        return wrapper
     
    @decorate
    def test(value):
        print 'value dans test : ', value
     
     
    @decorate
    def test1(value):
        print 'value dans test : ', value
     
    test(3)
    test(4)
     
    test1(40)
    L'appel du décorateur se produit en plusieurs étapes.

    - Une première fois lors de la déclaration du décorateur, c'est à dire la ligne @<quelque chose>, cette parti appelle le code de la fonction <quelque chose> et renvoi un wrapper, c'est à dire une autre fonction.

    - Lors de chaque appel à la fonction décorée, cela génère un appel au code de la fonction qui a été renvoyé à l'étape 1.

    Sur l'exemple précédent, j'ai d'abord 2 appels à la fonction decorate (vu que j'ai 2 fonctions décorées) qui renvois chaque fois une fonction (wrapper dans notre cas).

    A chaque appel de mes fonctions test, un appel se produit avant au wrapper et c'est lui qui est reponsable de l'appel réel de la fonction.


    Exemple plus réaliste, même s'il n'est pas génial. Vérification du type du paramètre passé qui doit être de type int :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    from types import *
     
    def verif_arg1_int(f):
        def wrapper(*args):
            if len(args) != 1:
                raise Exception('%s accept only 1 parameter' % f.__name__)
            if type(args[0]) is IntType:
                return f(*args)
            else:
                raise Exception('Parameter must be Int Type.')
        return wrapper
     
    @verif_arg1_int
    def mult2(value):
        print value * 2
     
     
    mult2(12)
    mult2('aa')
    Il existe 2 syntaxes pour les décorateurs.

    Python 2.4 et > :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    @verif_arg1_int
    def mult2(value):
        print value * 2
    Python 2.3 et avant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    def mult2(value):
        print value * 2
    mult2 = verif_arg1_int(mult2)
    La syntaxe python 2.4 est toujours utilisable en 2.5

  4. #4
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Très convaincant l'exemple avec Factorielle. On voit qu'on peut réutiliser des calculs déjà effectués. Maintenant j'avoue que je ne vois pas si j'aurais besoin de ce genre de fonctionnalités. Mais bon je garde cela en tête au cas où.

    Merci pour toutes ces infos.

Discussions similaires

  1. Réponses: 2
    Dernier message: 09/07/2010, 13h46
  2. RAID 0, 1 et 5 sur le même NAS, à quoi cela sert-ils ?
    Par beegees dans le forum Windows XP
    Réponses: 3
    Dernier message: 12/03/2010, 12h16
  3. [Remoting] Question pratique: à quoi cela sert-il ?
    Par bootix dans le forum Framework .NET
    Réponses: 6
    Dernier message: 17/03/2006, 16h16
  4. [XSD] A quoi cela sert-il ? Comment l'utiliser ?
    Par s3r3nity dans le forum Valider
    Réponses: 1
    Dernier message: 18/12/2005, 00h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo