2016-12-05 1 views
4

Je tiens d'abord à souligner que j'ai effectué une recherche sur le web en général et sur la documentation Python + StackOverflow de manière très approfondie et que je n'ai pas trouvé de réponse à cette question. Je tiens également à remercier toute personne qui prend le temps de lire ceci.Comment puis-je ajouter des arguments à une fonction enveloppée dans Python 2.7?

Comme le suggère le titre, je vous écris un décorateur en Python, et je le veux ajouter des arguments de mots clés à la fonction enveloppée (s'il vous plaît noter: Je sais comment ajouter des arguments au décorateur lui-même, ce n'est pas ce que je je demande).

Voici un exemple de travail d'un morceau de code que j'ai écrit qui fait exactement cela pour Python 3 (en particulier Python 3.5). Il utilise des arguments de décorateur, ajoute des arguments de mot-clé à la fonction enveloppée et définit et ajoute une nouvelle fonction à la fonction enveloppée.

from functools import wraps 

def my_decorator(decorator_arg1=None, decorator_arg2=False): 
    # Inside the wrapper maker 

    def _decorator(func): 
     # Do Something 1 

     @wraps(func) 
     def func_wrapper(
       *args, 
       new_arg1=False, 
       new_arg2=None, 
       **kwds): 
      # Inside the wrapping function 
      # Calling the wrapped function 
      if new_arg1: 
       return func(*args, **kwds) 
      else: 
       # do something with new_arg2 
       return func(*args, **kwds) 

     def added_function(): 
      print("Do Something 2") 

     func_wrapper.added_function = added_function 
     return func_wrapper 

    return _decorator 

Maintenant, ce décorateur peut être utilisé de la manière suivante:

@my_decorator(decorator_arg1=4, decorator_arg2=True) 
def foo(a, b): 
    print("a={}, b={}".format(a,b)) 

def bar(): 
    foo(a=1, b=2, new_arg1=True, new_arg2=7) 
    foo.added_function() 

Maintenant, alors que cela fonctionne pour Python 3.5 (et je suppose pour tout 3.x), je ne l'ai pas réussi à faire cela fonctionne pour Python 2.7. J'obtiens un SyntaxError: invalid syntax sur la première ligne qui essaye de définir un nouvel argument de mot-clé pour le func_wrapper, signifiant la ligne indiquant new_arg1=False,, en important le module contenant ce code. Le déplacement des nouveaux mots-clés au début de la liste d'arguments de func_wrapper résout le problème SyntaxError mais semble viser avec la signature de la fonction enveloppée; Je reçois maintenant l'erreur TypeError: foo() takes exactly 2 arguments (0 given) lors de l'appel foo(1, 2). Cette erreur disparaît si j'attribue les arguments explicitement, comme dans foo(a=1, b=2), mais ce n'est évidemment pas suffisant - sans surprise, mes nouveaux arguments de mot-clé semblent "voler" les deux premiers arguments positionnels envoyés à la fonction enveloppée. C'est quelque chose qui n'est pas arrivé avec Python 3.

J'aimerais recevoir votre aide à ce sujet. Merci de prendre du temps pour lire ceci.

Shay

+1

Le code ne fonctionne pas comme-est: vous devez importer functools, et une déclaration (peut-être l'impression ("added_function")) dans added_function. –

+0

@RoryYorke Édité! Merci, personne! :) – ShayPal5

+1

Comme c'est drôle que je posais exactement cette question exactement au même moment (enfin 20 minutes après vous). C'est bien que j'ai revu les "questions qui ont peut-être déjà votre réponse" après avoir fini de poser des questions et avant de pousser le bouton "postez votre question". – Davide

Répondre

3

Si vous ne spécifiez jamais les arguments supplémentaires comme mots-clés, vous pouvez les faire sortir du dictionnaire kw (voir ci-dessous). Si vous en avez besoin comme arguments de mot-clé positionnels, alors je pense que vous devriez pouvoir utiliser inspect.getargspec sur la fonction d'origine, puis traiter args et kw dans func_wrapper. Code ci-dessous testé sur Ubuntu 14.04 avec Python 2.7, 3.4 (fourni avec Ubuntu) et 3.5 (à partir de Continuum).

from functools import wraps 

def my_decorator(decorator_arg1=None, decorator_arg2=False): 
    # Inside the wrapper maker 

    def _decorator(func): 
     # Do Something 1 
     @wraps(func) 
     def func_wrapper(
       *args, 
       **kwds): 
      # new_arg1, new_arg2 *CANNOT* be positional args with this technique 
      new_arg1 = kwds.pop('new_arg1',False) 
      new_arg2 = kwds.pop('new_arg2',None) 
      # Inside the wrapping function 
      # Calling the wrapped function 
      if new_arg1: 
       print("new_arg1 True branch; new_arg2 is {}".format(new_arg2)) 
       return func(*args, **kwds) 
      else: 
       print("new_arg1 False branch; new_arg2 is {}".format(new_arg2)) 
       # do something with new_arg2 
       return func(*args, **kwds) 

     def added_function(): 
      # Do Something 2 
      print('added_function') 

     func_wrapper.added_function = added_function 
     return func_wrapper 

    return _decorator 

@my_decorator(decorator_arg1=4, decorator_arg2=True) 
def foo(a, b): 
    print("a={}, b={}".format(a,b)) 

def bar(): 
    pass 
    #foo(1,2,True,7) # won't work 
    foo(1, 2, new_arg1=True, new_arg2=7) 
    foo(a=3, b=4, new_arg1=False, new_arg2=42) 
    foo(new_arg2=-1,b=100,a='AAA') 
    foo(b=100,new_arg1=True,a='AAA') 
    foo.added_function() 

if __name__=='__main__': 
    import sys 
    sys.stdout.flush() 
    bar() 

sortie est

new_arg1 True branch; new_arg2 is 7 
a=1, b=2 
new_arg1 False branch; new_arg2 is 42 
a=3, b=4 
new_arg1 False branch; new_arg2 is -1 
a=AAA, b=100 
new_arg1 True branch; new_arg2 is None 
a=AAA, b=100 
added_function 
+0

Cela fait parfaitement sens, merci!Le seul problème que j'ai avec cela est que les nouveaux arguments de mots clés sont des citoyens de seconde classe; vous devez lire le code pour savoir qu'ils sont là et qu'ils sont des arguments à utiliser dans l'appel de fonction. Dans l'exemple de code Python 3, leur statut en tant qu'arguments de mot-clé est plus lisible et plus clair à partir du code. C'est bien sûr un problème avec Python 2, pas avec votre réponse. : P – ShayPal5

+0

Btw, non - I ** n'en ai pas besoin ** comme arguments positionnels. :) – ShayPal5

+0

Les arguments ajoutés ne fonctionneront pas non plus comme des arguments positionnels dans Python 3: '* args' capturera tous les arguments positionnels et la syntaxe que vous avez utilisée a été introduite pour ajouter des arguments uniquement pour les mots-clés: https://www.python.org/dev/peps/pep-3102/ –