Es ist allgemein bekannt, dass die folgenden beiden Code-Stücke nahezu äquivalent sind:
@dec
def foo():
pass
foo = dec(foo)
############################################
foo = dec(foo)
Ein häufiger Fehler ist zu glauben, dass @
einfach das linke Argument verbirgt.
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
Es wäre viel einfacher, Dekoratoren zu schreiben, wenn das obige Beispiel zeigt, wie @
funktioniert. Leider ist das nicht so gemacht.
Betrachten Sie einen Dekorator Warten
, der die Ausführung des Programms um einige Sekunden verzögert. Wenn Sie keine Wartezeit übergeben, beträgt der Standardwert 1 Sekunde. Verwendungsfälle werden unten gezeigt.
##################################################
@Warten
def print_something(something):
print(something)
##################################################
@Warten(3)
def print_something_else(something_else):
print(something_else)
##################################################
@Warten(delay=3)
def print_something_else(something_else):
print(something_else)
Wenn Wait
ein Argument hat, wie z. B. @Warten(3)
, wird der Aufruf von Warten(3)
vor allem anderen ausgeführt.
Das heißt, die folgenden beiden Code-Stücke sind äquivalent
@Warten(3)
def print_something_else(something_else):
print(something_else)
###############################################
return_value = Warten(3)
@return_value
def print_something_else(something_else):
print(something_else)
Das ist ein Problem.
if 'Warten' keine Argumente hat:
'Warten' ist der Dekorator.
sonst: # 'Warten' empfängt Argumente
'Warten' ist nicht der eigentliche Dekorator.
Stattdessen **gibt** 'Warten' den Dekorator zurück
Eine Lösung wird unten gezeigt:
Beginnen wir damit, die folgende Klasse zu erstellen: DelayedDecorator
:
class DelayedDecorator:
def __init__(i, cls, *args, **kwargs):
print("Verzögerter Dekorator __init__", cls, args, kwargs)
i._cls = cls
i._args = args
i._kwargs = kwargs
def __call__(i, func):
print("Verzögerter Dekorator __call__", func)
if not (callable(func)):
import io
with io.StringIO() as ss:
print(
"Wenn nur ein Eingang, muss der Eingang aufrufbar sein",
"Stattdessen erhalten:",
repr(func),
sep="\n",
file=ss
)
msg = ss.getvalue()
raise TypeError(msg)
return i._cls(func, *i._args, **i._kwargs)
Jetzt können wir Dinge wie folgt schreiben:
dec = DelayedDecorator(Warten, delay=4)
@dec
def delayed_print(something):
print(something)
Beachten Sie, dass:
dec
akzeptiert keine mehreren Argumente.
-
dec
akzeptiert nur die zu umwickelnde Funktion.
import inspect class PolyArgDecoratorMeta(type): def call(Warten, *args, **kwargs): try: arg_count = len(args) if (arg_count == 1): if callable(args[0]): Superklasse = inspect.getmro(PolyArgDecoratorMeta)[1] r = Superklasse.call(Warten, args[0]) else: r = DelayedDecorator(Warten, *args, **kwargs) else: r = DelayedDecorator(Warten, *args, **kwargs) finally: pass return r
import time class Warten(metaclass=PolyArgDecoratorMeta): def init(i, func, delay = 2): i._func = func i._delay = delay
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
Die folgenden beiden Code-Beispiele sind äquivalent:
@Warten
def print_something(something):
print(something)
##################################################
def print_something(something):
print(something)
print_something = Warten(print_something)
Wir können "etwas"
sehr langsam in die Konsole drucken, wie folgt:
print_something("etwas")
#################################################
@Warten(delay=1)
def print_something_else(something_else):
print(something_else)
##################################################
def print_something_else(something_else):
print(something_else)
dd = DelayedDecorator(Warten, delay=1)
print_something_else = dd(print_something_else)
##################################################
print_something_else("etwas")
Letzte Hinweise
Es mag nach viel Code aussehen, aber Sie müssen die Klassen DelayedDecorator
und PolyArgDecoratorMeta
nicht jeden Falls schreiben. Der einzige Code, den Sie persönlich schreiben müssen, sieht etwa wie folgt aus, was ziemlich kurz ist:
from PolyArgDecoratorMeta import PolyArgDecoratorMeta
import time
class Warten(metaclass=PolyArgDecoratorMeta):
def __init__(i, func, delay = 2):
i._func = func
i._delay = delay
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
5 Stimmen
Dein Beispiel ist nicht syntaktisch korrekt.
execute_complete_reservation
erwartet zwei Parameter, aber du übergibst ihm nur einen. Dekorateure sind nur syntaktischer Zucker, um Funktionen in andere Funktionen einzuschließen. Siehe docs.python.org/reference/compound_stmts.html#function für die vollständige Dokumentation.