Je sais que cette question est ancienne, mais certains commentaires sont nouveaux, et alors que toutes les autres solutions viables sont essentiellement les mêmes, la plupart d'entre eux ne sont pas très propre et facile à lire.
Comme thobe la réponse dit, la seule façon de gérer les deux cas est de vérifier pour les deux scénarios. La façon la plus simple est tout simplement de vérifier pour voir si il n'y a qu'un seul argument et il est callabe (REMARQUE: les contrôles supplémentaires seront nécessaires si votre décorateur prend seulement 1 argument et il arrive à être un objet appelable):
def decorator(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# called as @decorator
else:
# called as @decorator(*args, **kwargs)
Dans le premier cas, vous faites ce que tout le décorateur n', de retour d'une modification ou enveloppé version de la réussite dans la fonction.
Dans le second cas, vous revenez à un "nouveau" décorateur qui utilise en quelque sorte les informations transmises avec *args, **kwargs.
C'est très bien et tout, mais d'avoir à l'écrire pour chaque décorateur peut être assez ennuyeux et pas aussi propre. Au lieu de cela, il serait agréable d'être en mesure de modifier automatiquement nos décorateurs sans avoir à ré-écrire... mais c'est ce que les décorateurs sont pour!
Suivant le décorateur le décorateur, nous pouvons deocrate nos décorateurs de sorte qu'ils peuvent être utilisés avec ou sans arguments:
def doublewrap(f):
'''
a decorator decorator, allowing the decorator to be used as:
@decorator(with, arguments, and=kwargs)
or
@decorator
'''
@wraps(f)
def new_dec(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# actual decorated function
return f(args[0])
else:
# decorator arguments
return lambda realf: f(realf, *args, **kwargs)
return new_dec
Maintenant, nous pouvons décorer nos décorateurs avec @doublewrap, et ils vont travailler avec et sans arguments, avec une mise en garde:
Je l'ai noté ci-dessus, mais doit se répéter ici, les contrôles dans ce décorateur de fait une hypothèse sur les arguments qu'un décorateur peut recevoir (à savoir qu'il ne peut pas recevoir un seul, rachetable argument). Puisque nous sommes le rendre applicable à tout générateur de maintenant, il doit être gardé à l'esprit, ou modifiée si elle va être contredit.
Ce qui suit démontre son utilisation:
def test_doublewrap():
from util import doublewrap
from functools import wraps
@doublewrap
def mult(f, factor=2):
'''multiply a function's return value'''
@wraps(f)
def wrap(*args, **kwargs):
return factor*f(*args,**kwargs)
return wrap
# try normal
@mult
def f(x, y):
return x + y
# try args
@mult(3)
def f2(x, y):
return x*y
# try kwargs
@mult(factor=5)
def f3(x, y):
return x - y
assert f(2,3) == 10
assert f2(2,5) == 30
assert f3(8,1) == 5*7