Comment utiliser des décorateurs en Perl ?
Un tutoriel de Laurent Rosenfeld
Le 2017-11-29 19:48:11, par Lolo78, Rédacteur/Modérateur
Bonsoir,
j'ai le plaisir d'annoncer la publication sur ce site d'un nouvel article: Comment utiliser des décorateurs en Perl - Un tutoriel pour changer le comportement d'une fonction sans en modifier le code source.
Un « décorateur » est une fonction qui permet de modifier le comportement d'une autre fonction sans toucher au code de cette autre fonction. Cela permet notamment d'ajouter des traces d'exécution (par exemple à des fins de débogage) ou d'accélérer le fonctionnement d'une fonction en ajoutant un cache.
Perl n'a pas de fonctionnalité spécifique pour utiliser des décorateurs, mais cet article montre qu'il est assez facile de créer cette fonctionnalité. Au passage, l'article illustre l'utilisation de certaines fonctions dites « avancées » de Perl, lesquelles sont en fait moins mystérieuses qu'il n'y paraît de prime abord.
Bonne lecture et bonne soirée à tous.
j'ai le plaisir d'annoncer la publication sur ce site d'un nouvel article: Comment utiliser des décorateurs en Perl - Un tutoriel pour changer le comportement d'une fonction sans en modifier le code source.
Un « décorateur » est une fonction qui permet de modifier le comportement d'une autre fonction sans toucher au code de cette autre fonction. Cela permet notamment d'ajouter des traces d'exécution (par exemple à des fins de débogage) ou d'accélérer le fonctionnement d'une fonction en ajoutant un cache.
Perl n'a pas de fonctionnalité spécifique pour utiliser des décorateurs, mais cet article montre qu'il est assez facile de créer cette fonctionnalité. Au passage, l'article illustre l'utilisation de certaines fonctions dites « avancées » de Perl, lesquelles sont en fait moins mystérieuses qu'il n'y paraît de prime abord.
Bonne lecture et bonne soirée à tous.
-
djibrilResponsable Perl et OutilsBonsoir,
Encore un très bon tutoriel sur Perl qui nous apprend de nouvelles techniques de programmation avancées.le 29/11/2017 à 20:59 -
dca_marshallNouveau membre du ClubBonjour,
Excellent 👍
Jusque la, j'utilisais un Wrapper pour se charger des compteurs d'appel, des code-retours et des compteurs internes à la fonction (avec PadWalker).
Je vais donc revoir ma copie...
Merci pour ce cours et sa modernité.le 30/11/2017 à 11:59 -
ptonnerreMembre habituéLa lecture du début de ce tutoriel, notamment les premiers exemples d'utilisation de cache, m'a fait réfléchir à un de mes scripts.
J'ai pu ainsi optimiser un traitement mensuel qui dure en moyenne 1h27 à 52 minutes.
Pas mal non !
Encore un grand merci à Laurent pour ses tutorielsle 17/12/2017 à 10:33 -
ptonnerreMembre habituéUn nouveau tuto que je vais lire dès que possible.
le 04/12/2017 à 11:13 -
ptonnerreMembre habituéBonjour,
j'essaye de mettre en oeuvre les décorateurs avec ce cas d'application simple : nos programmes écrivent dans un fichier de log qui sera poussé vers un serveur de logs. Les lignes du fichier log ont un format spécifique et les écritures sont gérées par des fonctions d'un module.
Les fonctions gérant les lignes de type INFO et WARN sont similaires, à l'exception du préfixe de la ligne (OXRES ou OXINC).
Version sans décorateurs :Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17sub oxa_msg_info { my ( $msg, $flag ) = @_; my $tms = ( $flag ) ? utils_timestamp() . ' - ' : ''; say 'OXRES: ' . $tms . $msg; return; } sub oxa_msg_warn { my ( $msg, $flag ) = @_; my $tms = ( $flag ) ? utils_timestamp() . ' - ' : ''; say "OXINC: " . $tms . "$msg"; return; }
Cette version crée en début de module deux coderefs via l'appel de la fonction decorateur en lui passant en argument la référence à la fonction générique info_or_warn qui ne fait qu'une écriture et le préfixe associé.
Nous avons donc à disposition les deux fonctions oxa_msg_info et oxa_msg_warn utilisant la fonction générique info_or_warn redécorée en fonction du besoin .Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19{ no warnings 'redefine'; *{xxx_utils::oxa_msg_info} = decorateur(\&info_or_warn, 'OXRES'); *{xxx_utils::oxa_msg_warn} = decorateur(\&info_or_warn, 'OXINC'); } sub info_or_warn { my ( $msg ) = @_; say $msg; } sub decorateur { my ( $coderef, $type_msg) = @_; return sub { my ( $msg, $flag ) = @_; my $tms = ( $flag ) ? utils_timestamp() . ' - ' : ''; $coderef->($type_msg . ': ' . $tms . $msg); } }
Commentaires/suggestions bienvenus.le 19/01/2018 à 11:10 -
Lolo78Rédacteur/ModérateurOK, dans ce cas, tu peux sans doute créer ta fonction directement dans la table des symboles sans pour autant avoir besoin d'utiliser de décorateur:
Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17use strict; use warnings; use feature "say"; sub create_oxa_message { my $ox_what = shift; return sub { my $msg = shift; say "$ox_what: TMS $msg"; } } *::oxa_msg_info = create_oxa_message ("OXRES: "); *::oxa_msg_warn = create_oxa_message ("OXINC: "); oxa_msg_info("Information"); oxa_msg_warn("Avertissement");
Code : 1
2
3OXRES: : TMS Information OXINC: : TMS Avertissement
Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20use strict; use warnings; use feature "say"; sub create_oxa_message { my $ox_what = shift; return sub { my $msg = shift; say "$ox_what: TMS $msg"; } } my %hash = (info => "OXRES: ", warn => "OXINC: "); { no strict "refs"; *{"::oxa_msg_" . $_} = create_oxa_message ($hash{$_}) for keys %hash; } oxa_msg_info("Information"); oxa_msg_warn("Avertissement");
La syntaxe devient un peu plus complexe et moins claire (voire quelque peu inélégante), mais ça peut être intéressant si tu as beaucoup de types de messages (en plus de info et warn).
Hormis cela, est-ce que le codage des décorateurs te paraît correct ?
le 20/01/2018 à 18:19 -
Lolo78Rédacteur/ModérateurEn y réfléchissant, ce que j'ai proposé dans mon dernier post n'est en définitive pas très différent de ce que tu as fait à l'origine dans le tien, même si ce n'est pas fait exactement de la même façon (ma version est quand même un peu plus simple, me semble-t-il).
J'ai été un peu trompé par le nom (decorateur) de ta fonction, mais ce que tu fais dans ton code n'est pas tout-à-fait un décorateur dans la mesure où tu ne remplaces pas une fonction existante par une autre (du moins dans le sens que j'ai donné su mot dans le tutoriel), mais te contentes en fait de créer de nouvelles fonctions en utilisant une fonction tout juste créée (note que, du coup, tu n'as semble-t-il pas besoin du pragma no warnings 'redefine';, puisque tu ne redéfinis pas une fonction existante (à toi de vérifier avec ton code, mais en tous cas, je n'en ai bas besoin avec le mien). Cela dit, ton code utilise bien le même principe que les décorateurs décrits dans le tuto.
C'est un usage qui peut être utile et auquel je n'avais pas vraiment pensé quand j'ai rédigé le tuto. J'y réfléchirai, mais peut-être que j'ajouterai un paragraphe sur ce type d'utilisation des techniques employées pour créer mes décorateurs. Merci à toi de me donner une piste d'enrichissement du tutoriel.le 20/01/2018 à 19:41 -
ptonnerreMembre habituéJe confirme que le pragma no warnings 'redefine' n'est pas nécessaire.
Ta version qui écrit directement dans la table des symboles est effectivement plus simple et je vais la retenir, tout en gardant la mienne pour mémo, version qui mélangeait allègrement, il me semble, décorateurs et usine à fonctions
Quoi qu'il en soit, ce tuto ouvre de nombreuses voies de réflexion et j'espère qu'il sera utile à beaucoup d'entre nous.
le 22/01/2018 à 13:41 -
Lolo78Rédacteur/ModérateurBonjour,
suite à la discussion avec ptonnerre sur ces utilisations de la table des symboles (hors décorateurs), j'ai ajouté une nouvelle section (§ 4.3) dans le tutoriel.
Bonne lecture à tous et merci à ptonnerre.
Bonne soirée,
Laurent.le 30/01/2018 à 19:55 -
Lolo78Rédacteur/ModérateurMerci du compliment.
Si ce n'est pas confidentiel et si ce n'est pas trop compliqué, il serait peut-être intéressant que tu publies un post illustrant cette optimisation. Ça pourrait rendre service à d'autres.le 17/12/2017 à 11:24