Le wrapper suivant rend la fonction mise en cache plus proche de l'originale.
from functools import wraps
def restore(func):
@wraps(func.__wrapped__)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
Cela crée encore un autre wrapper autour de votre fonction décorée qui restaure son type en tant que fonction tout en préservant la docstring.
Par exemple, si vous avez une fonction comme ceci :
@restore
@lru_cache
def func_dup(x: int):
"""ma doc"""
return x
Ensuite, exécutez help(func_dup)
Aide sur la fonction func_dup dans le module __main__:
func_dup(x: int)
ma doc
Pourquoi la différence
Je vais utiliser CPython 3.10, qui est la dernière version au moment où j'ai écrit cette réponse.
La fonction help
est en fait implémentée dans pydoc
en tant qu'objet Helper
. La méthode magique Helper.__call__
est définie pour appeler Helper.help
. Elle appelle ensuite doc
, qui appelle render_doc
. La fonction render_doc
compose la chaîne qui est imprimée. À l'intérieur de cette fonction, elle appelle pydoc.describe
pour un nom descriptif de votre fonction.
Votre mymodule.myfunction
original est une fonction, donc describe
retourne dans cette branche.
if inspect.isfunction(thing):
return 'function ' + thing.__name__
Cela donne "function myfunction"
.
Cependant, après avoir décoré votre fonction avec @lru_cache
, elle devient une instance du type intégré/extension functools._lru_cache_wrapper
. Je ne suis pas sûr pourquoi c'est implémenté de cette manière, mais la fonction décorée n'est plus de type types.FunctionType
. Ainsi, la fonction describe(mymodule.myfunction)
retourne à la dernière ligne après avoir été décorée.
return type(thing).__name__
Cela renvoie "_lru_cache_wrapper"
.
La fonction functools.update_wrapper
tente de
Mettre à jour une fonction wrapper pour ressembler à la fonction enveloppée
Elle ne restaure pas le wrapper en tant qu'instance de types.FunctionType
. Cependant, elle fait référence à la fonction originale dans l'attribut __wrapped__
. Ainsi, nous pouvons utiliser cela pour envelopper à nouveau votre fonction originale.
Référence
Il y a un problème Python bpo-46761 qui peut ou non être lié à ce problème.
lorsque vous utilisez functools.partial() pour pré-fournir des arguments à une fonction, si vous appelez ensuite functools.update_wrapper() pour mettre à jour cet objet partiel, inspect.signature() retourne la signature de la fonction originale, pas la signature de la fonction enveloppée.
C'est principalement sur functools.partial
, qui ne préserve même pas la signature de la fonction enveloppée.
0 votes
Est-ce que cela répond à votre question? Python decorator handling docstrings
1 votes
@SpearAndShield merci pour le conseil, mais je pense que cela ne s'applique pas à mon cas. J'ai mis à jour la question
1 votes
La fonction est désormais le résultat du décorateur. Si les personnes qui ont créé la fonction n'ont pas ajouté
wraps
(ou ne l'ont pas fait manuellement) alors je ne pense pas qu'il y ait quelque chose que vous puissiez faire, à moins d'écrire votre propre version du décorateur qui le fait (peut-être en le reportant à celui qui ne fait pas ce que vous voulez).1 votes
Mais je pense que c'est en fait un problème avec
help