119 votes

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) pour x dans args]
 kwargs = dict((k, int(v)) for k, v in kwargs.items())
 de retour de f(*args, **kwargs)
 de retour g

@args_as_ints
def funny_function(x, y, z=3):
 """Calcule x*y + 2*z"""
 return x*y + 2*z

>>> funny_function("3", 4.0, z="5")
22

Tout bien 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)
De l'aide sur la fonction g dans le module __principaux__:

g(*args, **kwargs)

Heureusement, il existe une solution de contournement:

def args_as_ints(f):
 def g(*args, **kwargs):
 args = [int(x) pour x dans args]
 kwargs = dict((k, int(v)) for k, v in kwargs.items())
 de retour de f(*args, **kwargs)
 g.__name__ = f.__nom__
 g.__doc__ = f.__doc__
 de retour g

@args_as_ints
def funny_function(x, y, z=3):
 """Calcule 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)
De l'aide sur la fonction funny_function dans le module __principaux__:

funny_function(*args, **kwargs)
 Calcule 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.

Que 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) -- calcule x*y + 2*z"""
 return x*y + 2*z

C'est mauvais à cause 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):
 de retour de f(int(x), int(y), z=int(z))
 g.__name__ = f.__nom__
 g.__doc__ = f.__doc__
 de retour 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 -- exec?

Toutes les autres approches?

89voto

J.F. Sebastian Points 102961
  1. Installer décorateur module:

    $ easy_install decorator
    
  2. Adapter la définition de l' args_as_ints():

    import decorator
    
    
    @decorator.decorator
    def args_as_ints(f, *args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    
    
    @args_as_ints
    def funny_function(x, y, z=3):
        """Computes x*y + 2*z"""
        return x*y + 2*z
    
    
    print funny_function("3", 4.0, z="5")
    # 22
    help(funny_function)
    # Help on function funny_function in module __main__:
    # 
    # funny_function(x, y, z=3)
    #     Computes x*y + 2*z
    

9voto

DzinX Points 13452

Il y a un décorateur module avec decorator décorateur, vous pouvez utiliser:

@decorator
def args_as_ints(f, *args, **kwargs):
    args = [int(x) for x in args]
    kwargs = dict((k, int(v)) for k, v in kwargs.items())
    return f(*args, **kwargs)

Ensuite, la signature et à l'aide de la méthode est conservée:

>>> help(funny_function)
Help on function funny_function in module __main__:

funny_function(x, y, z=3)
    Computes x*y + 2*z

EDIT: J. F. Sebastian remarquer que je n'ai pas modifier args_as_ints , est fixé maintenant.

8voto

Brian Points 48423

Jetez un oeil à le décorateur module spécifiquement le décorateur le décorateur, ce qui résout ce problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X