Ich weiß, dass diese Frage alt ist, aber einige der Kommentare sind neu, und obwohl alle praktikablen Lösungen im Wesentlichen gleich sind, sind die meisten von ihnen nicht sehr sauber oder leicht zu lesen.
Wie die Antwort von thobe sagt, besteht die einzige Möglichkeit, beide Fälle zu behandeln, darin, nach beiden Szenarien zu suchen. Der einfachste Weg ist einfach zu überprüfen, ob es ein einzelnes Argument gibt und es callabe ist (HINWEIS: Zusätzliche Überprüfungen sind erforderlich, wenn Ihr Dekorateur nur 1 Argument akzeptiert und es sich zufällig um ein aufrufbares Objekt handelt):
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)
Im ersten Fall tun Sie das, was ein normaler Dekorateur tut, und geben eine modifizierte oder umschlossene Version der übergebenen Funktion zurück.
Im zweiten Fall geben Sie einen 'neuen' Dekorateur zurück, der die mit * args, ** kwargs übergebenen Informationen irgendwie verwendet.
Das ist in Ordnung und alles, aber es für jeden Dekorateur, den Sie machen, aufschreiben zu müssen, kann ziemlich nervig und nicht so sauber sein. Stattdessen wäre es schön, unsere Dekorateure automatisch modifizieren zu können, ohne sie neu schreiben zu müssen ... aber dafür sind Dekorateure da!
Mit dem folgenden Dekorateur können wir unsere Dekorateure so dekrieren, dass sie mit oder ohne Argumente verwendet werden können:
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
Jetzt können wir unsere Dekorateure mit @doublewrap dekorieren und sie werden mit und ohne Argumente mit einer Einschränkung arbeiten:
Ich habe oben erwähnt, sollte aber hier wiederholen, dass die Prüfung in diesem Dekorateur eine Annahme über die Argumente macht, die ein Dekorateur erhalten kann (nämlich dass er kein einziges, aufrufbares Argument empfangen kann). Da wir es jetzt auf jeden Generator anwendbar machen, muss es berücksichtigt oder modifiziert werden, wenn es widersprochen wird.
Das Folgende demonstriert seine Verwendung:
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
@redirect_output
ist bemerkenswert wenig aussagekräftig. Ich würde vorschlagen, dass es eine schlechte Idee ist. Verwenden Sie die erste Form und vereinfachen Sie Ihr Leben sehr.