Supposons que j'ai écrit un décorateur qui fait quelque chose de très générique. Par exemple, il peut convertir tous les arguments en un type spécifique, effectuer la journalisation, implémenter la mémorisation, etc.
Voici un exemple:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
>>> funny_function("3", 4.0, z="5")
22
Tout va bien jusqu'à présent. Il y a cependant un problème. La fonction décorée ne conserve pas la documentation de la fonction d'origine:
>>> help(funny_function)
Help on function g in module __main__:
g(*args, **kwargs)
Heureusement, il existe une solution de contournement:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
Cette fois, le nom de la fonction et la documentation sont corrects:
>>> help(funny_function)
Help on function funny_function in module __main__:
funny_function(*args, **kwargs)
Computes x*y + 2*z
Mais il y a toujours un problème: la signature de la fonction est erronée. L'information "* args, ** kwargs" est presque inutile.
Que faire? Je peux penser à deux solutions de contournement simples mais imparfaites:
1 - Incluez la signature correcte dans la docstring:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
C'est mauvais à cause de la duplication. La signature ne sera toujours pas affichée correctement dans la documentation générée automatiquement. Il est facile de mettre à jour la fonction et d'oublier de changer la docstring ou de faire une faute de frappe. [ Et oui, je suis conscient du fait que la docstring duplique déjà le corps de la fonction. Veuillez ignorer ceci; funny_function est juste un exemple aléatoire. ]
2 - Ne pas utiliser de décorateur, ni utiliser de décorateur spécial pour chaque signature spécifique:
def funny_functions_decorator(f):
def g(x, y, z=3):
return f(int(x), int(y), z=int(z))
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
Cela fonctionne bien pour un ensemble de fonctions qui ont une signature identique, mais c'est inutile en général. Comme je l'ai dit au début, je veux pouvoir utiliser les décorateurs de manière entièrement générique.
Je recherche une solution tout à fait générale et automatique.
La question est donc la suivante: existe-t-il un moyen d'éditer la signature de la fonction décorée après sa création?
Sinon, puis-je écrire un décorateur qui extrait la signature de la fonction et utilise cette information au lieu de "* kwargs, ** kwargs" lors de la construction de la fonction décorée? Comment extraire ces informations? Comment dois-je construire la fonction décorée - avec exec?
D'autres approches?
inspect.Signature
ajoutait à la gestion des fonctions décorées.