Merci pour la prise en compte de mes remarques. Je reprendrai depuis le début une fois la première passe de relecture terminée. Passons aux décorateurs.
Introduction
Un décorateur permet de se soustraire à un objet pour en modifier le comportement.
Je ne comprends pas tellement l'expression "se soustraire à".
D&CO, une semaine pour tout changer
afin de retourner un nouvel objet (généralement appelable). Un décorateur est donc une fonction prenant une fonction en paramètre et retournant une fonction.
Je chipote, mais le "et retournant une fonction" n'est pas tellement en phase avec la parenthèse qui précède.
Nous utilisons ici un décorateur très simple qui retourne la même fonction, mais il se pourrait très bien qu'il en retourne une autre, qui pourrait être créée à la volée.
Aurais-tu un exemple de décorateur retournant la fonction qu'il reçoit et plus utile que celui que tu donnes ?
Décorateurs paramétrés
En fait, @ ne doit pas nécessairement être suivi d'un nom d'objet, des arguments peuvent aussi s'y ajouter à l'aide de parenthèses.
La formulation est un peu étrange. Il me semble plus clair de dire que @
est suivi d'un décorateur et que make_dec(*args)
en est un.
Envelopper une fonction
Mais vous pouvez choisir de leur donner un sens, nous le ferons dans le TP suivant. Une utilisation courante est de préciser dans les annotation les types des paramètres et le type de retour.
A noter que c'est devenu une convention depuis Python 3.5.
Tout ce qu'il y a à savoir pour le moment, c'est qu'une annotation peut-être n'importe quel objet python
Avec un objet valide quand même.
TP : divers décorateurs
Nous allons pour cela utiliser les signatures de fonctions.
Je chipote, mais la formulation ne convient pas tellement. Tu pourrais plutôt avoir : "Pour pallier cela".
Nous allons maintenant nous intéresser à singledispatch, une implémentation de fonctions génériques permettant de dispatcher l'appel en fonction du type du premier paramètre.
Sans avoir lu la suite, je ne comprends pas cela.
Un décorateur, singledispatch prend une fonction en paramètre (c'est la fonction qui sera appelée si aucune spécialisation n'est trouvée), et en retourne un nouvel objet. Cet objet possède une méthode register qui s'utilisera comme un décorateur paramétré en lui précisant le type pour lequel nous voulons spécialiser.
J'ai bloqué sur ce passage, parce que je ne comprenais pas ce que tu entendais par spécialisation/spécialiser.
Je vous propose pour cette fois de réaliser notre décorateur à l'aide d'une classe.
Le code permet de comprendre ce que tu dis plus haut, mais son utilité n'est pas très claire. Aurais-tu un exemple d'application ?
Notre décorateur va donc se charger d'analyser les paramètres lors de chaque appel à la fonction
Les arguments plutôt, non ?
Certains langages implémentent l'optimisation dite de récursivité terminale : si l'appel récursif est la dernière instruction exécutée dans la fonction, il est possible de supprimer de la pile le contexte courant avant d'appeler la fonction suivante, et ainsi ne pas surcharger la pile. Ce n'est pas le cas avec Python.
Ce passage risque d'être compliqué pour qui n'a pas de connaissances en programmation fonctionnelle. A toi de voir si tu veux dédier cette application aux personnes familières avec la notion de récursivité terminale. Sinon, tu pourrais illustrer tes propos par un exemple.
Il faut bien noter que le retour de la méthode call ne sera pas utilisable comme le résultat réel de notre fonction, nous ne pourrons que le retourner pour qu'il soit ensuite évalué par le wrapper.
Je ne comprends pas trop cela.
Maintenant nous allons réaliser notre décorateur tail_rec, j'ai opté pour une classe :
J'ai pas mal bloqué sur le code, notamment sur la différence entre call
et __call__
. En passant du temps dessus, ça va mieux, mais ce n'est pas évident. J'ignore par contre s'il est judicieux d'ajouter des explications, ou s'il est préférable de laisser le lecteur se débrouiller.
Merci !