Bonjour,
tout est dans le titre.
Toute info. est la bienvenue.
Bonjour,
tout est dans le titre.
Toute info. est la bienvenue.
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:
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.
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
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:
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.>>> 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}
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.
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
Code : Sélectionner tout - Visualiser dans une fenêtre à part if args in f.cache():Le principe d'un décorateur est d'avoir un fonction qui est appellé avant la 'vrai' fonction.
Code : Sélectionner tout - Visualiser dans une fenêtre à part if args in f.cache:
Exemple :L'appel du décorateur se produit en plusieurs étapes.
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)
- 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 :
Il existe 2 syntaxes pour les décorateurs.
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')
Python 2.4 et > :
Python 2.3 et avant :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 @verif_arg1_int def mult2(value): print value * 2La syntaxe python 2.4 est toujours utilisable en 2.5
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 def mult2(value): print value * 2 mult2 = verif_arg1_int(mult2)
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.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager