2017-03-10 1 views
2

J'essaie actuellement des décorateurs et je ne peux pas comprendre une chose. J'ai simplement décorateur:Python - décorateur appelant la fonction interne?

def new_decorator(func): 
    print "Calling new_decorator()" 
    def wrap(arg): 
     print "Calling wrap()" 
     func(arg) 
    return wrap 

@new_decorator 
def foo(name): 
    print name 

Une fois que nous obtenons en cours d'exécution:

'Calling decorate()' 

seulement après avoir appelé foo:

>>> foo('Jon') 
Calling wrap() 
Jon 

Jusqu'à présent, je comprends tout (je pense). new_decorator(func) est appelé au début, avec foo comme paramètre et il renvoie wrap. Mais maintenant, j'ai essayé décorateurs avec des paramètres:

def add_attr(varname, value): 
    def decorate(func): 
     print "Calling decorate()" 
     setattr(func, varname, value) 
     return func 
    return decorate 

@add_attr('var', 'New attribute') 
def foo(): 
    print "Calling foo()" 
    print foo.var 

Lors de l'exécution (sans appeler foo()), il retourne:

'Calling decorate()' 

Après foo():

>>> foo() 
Calling foo() 
New attribute 

Ce que je ne comprends pas est , pourquoi cette fonction "interne" decorate s'appelle au départ, avant même d'appeler foo? Dans le premier exemple, ce n'était pas le cas. Et comment était la fonction foo envoyer en tant que paramètre à decorate? add_attr obtient seulement 2 paramètres, et aucun d'entre eux est foo.

+0

Peut-être parce que vous appelez *? Si vous avez ajouté des parenthèses arrondies autour de quelque chose que vous appelez cet élément ... –

+1

Le second cas est similaire à 'add_attr ('var', 'Nouvel attribut') (foo)' qui appelle déjà la décoration intérieure. –

+0

@MosesKoledoye Je comprends, merci! –

Répondre

2

Vous avez généralement besoin de 3 couches d'imbrication pour un décorateur qui accepte les arguments. Pensez à un décorateur qui accepte les arguments en tant que «décorateur». Il devrait retourner un décorateur lorsqu'il est appelé.

Pour comprendre cela, considérons que le code ci-dessous

@new_decorator 
def foo(name): 
    print name 

est vraiment juste sucre syntaxe pour cela:

def foo(name): 
    print name 

foo = new_decorator(foo) 

Ainsi, de même, ceci:

@add_attr('var', 'New attribute') 
def foo(): 
    print "Calling foo()" 
    print foo.var 

est-identique à ceci:

def foo(): 
    print "Calling foo()" 
    print foo.var 

foo = add_attr('var', 'New attribute')(foo) 

Cela devrait vous guider dans la bonne direction pour écrire correctement add_attr.

Si vous êtes encore confus après avoir fait quelques tentatives, je vous renvoie à a famous answer from e-satis qui explique mieux les décorateurs que tout ce que j'ai vu sur le web.

+0

Merci, maintenant je comprends! –