120 votes

Comment passer des arguments supplémentaires à Python Decorator?

J'ai un décorateur comme ci-dessous

 def myDecorator(test_func):
    return callSomeWrapper(test_func)
def callSomeWrapper(test_func):
    return test_func
@myDecorator
def someFunc():
    print 'hello'
 

Je veux améliorer ce décorateur pour accepter un autre argument comme ci-dessous

 def myDecorator(test_func,logIt):
    if logIt:
        print "Calling Function: " + test_func.__name__
    return callSomeWrapper(test_func)
@myDecorator(False)
def someFunc():
    print 'Hello'
 

Mais ce code donne l'erreur,

 TypeError: myDecorator() takes exactly 2 arguments (1 given)
 

Pourquoi la fonction n'est pas automatiquement transmise? Comment est-ce que je passe explicitement la fonction à la fonction décorateur?

203voto

interjay Points 51000

Depuis que vous appelez le décorateur, comme une fonction, elle doit retourner une autre fonction qui est la véritable décorateur:

def myDecorator(logIt):
    def actualDecorator(test_func):
        if logIt:
            print "Calling Function: " + test_func.__name__
        return callSomeWrapper(test_func)
    return actualDecorator

La fonction externe sera donné tous les arguments que vous avez passer explicitement, et doit renvoyer la fonction interne. La fonction interne sera passé à la fonction pour décorer, et le retour de la enveloppé fonction.

Notez que le message du journal ci-dessus est incorrecte, car il sera enregistré lorsque la fonction est décoré, et non pas lorsqu'elle est appelée. Ce que vous voulez peut-être quelque chose comme ceci:

def myDecorator(logIt):
    def actualDecorator(test_func):
        @functools.wraps(test_func)
        def wrapper(*args, **kwargs):
            if logIt:
                print "Calling Function: " + test_func.__name__
            return test_func(*args, **kwargs)
        return wrapper
    return actualDecorator

54voto

katrielalex Points 40655

Juste pour donner un point de vue différent: la syntaxe

@expr
def func(...): #stuff

est équivalent à

def func(...): #stuff
func = expr(func)

En particulier, expr peut être tout ce que vous voulez, du moment qu'il correspond à un appelable. En particulier en particulier, expr peut être un décorateur d'usine: vous donner quelques paramètres et il vous donne un décorateur. Alors peut-être une meilleure façon de comprendre votre situation est que

dec = decorator_factory(*args)
@dec
def func(...):

qui peut ensuite être réduit à

@decorator_factory(*args)
def func(...):

Bien sûr, depuis qu'il regarde comme l' decorator_factory est un décorateur, les gens ont tendance à le nommer pour refléter cela. Ce qui peut être déroutant lorsque vous essayez de suivre les niveaux d'indirection.

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