La préservation des signatures de fonctions décoré

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 d'un type spécifique, effectuer une journalisation, de mettre en œuvre memoization, 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

Bien le tout jusqu'à présent. Il y a un problème, cependant. Décoré de la fonction n'est pas de conserver 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 de 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 encore un problème: la signature de la fonction est mal. L'information "*args, **kwargs" est à côté de rien.

Quoi faire? Je pense à deux simple mais imparfait solutions de contournement:

1 -- Inclure 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

Ce qui est mauvais en raison de la duplication. La signature ne sera pas affiché correctement dans générée automatiquement de la documentation. Il est facile de mettre à jour la fonction et de l'oublier sur la modification de la docstring, ou de faire une faute de frappe. [Et oui, je suis conscient du fait que la docstring déjà doublons le corps de la fonction. S'il vous plaît ignorer ce; funny_function est juste un hasard.]

2 -- ne Pas utiliser un décorateur, ou utiliser à des fins spéciales de décorateur pour chaque signature:

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 très bien pour un ensemble de fonctions ayant la même signature, mais c'est inutile en général. Comme je l'ai dit au début, je veux être en mesure d'utiliser les décorateurs entièrement de façon générique.

Je suis à la recherche d'une solution qui est entièrement en général, et de l'automatique.

La question est donc: est-il un moyen de modifier la décorées signature de fonction après qu'il a été créé?

Sinon, puis-je écrire un décorateur que des extraits de la signature de la fonction et l'utilise à la place de "*kwargs, **kwargs" lors de la construction de la décorées fonction? Comment puis-je extraire cette information? Comment dois-je construire le décoré de la fonction -- avec exec?

Toutes les autres approches?

  • N'a jamais dit "out of date". J'étais plus ou moins demandais ce que inspect.Signature ajouté à traiter décorés avec des fonctions.