Es ist bekannt, dass die folgenden zwei Codeteile nahezu gleichwertig sind:
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
Ein häufiger Fehler ist zu denken, dass @
das Argument ganz links einfach verborgen bleibt .
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
Es wäre viel einfacher, Dekorateure zu schreiben, wenn das oben Genannte so @
funktioniert. Leider wird das nicht so gemacht.
Wait
Stellen Sie sich einen Dekorateur vor, der die Programmausführung für einige Sekunden übernimmt. Wenn Sie keine Wartezeit einhalten, beträgt der Standardwert 1 Sekunde. Anwendungsfälle sind unten aufgeführt.
##################################################
@Wait
def print_something(something):
print(something)
##################################################
@Wait(3)
def print_something_else(something_else):
print(something_else)
##################################################
@Wait(delay=3)
def print_something_else(something_else):
print(something_else)
Wenn Wait
ein Argument wie z. B. vorhanden ist @Wait(3)
, wird der Aufruf zuvorWait(3)
ausgeführt etwas anderes passiert.
Das heißt, die folgenden zwei Codeteile sind äquivalent
@Wait(3)
def print_something_else(something_else):
print(something_else)
###############################################
return_value = Wait(3)
@return_value
def print_something_else(something_else):
print(something_else)
Das ist ein Problem.
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
Eine Lösung ist unten gezeigt:
Beginnen wir mit der Erstellung der folgenden Klasse DelayedDecorator
:
class DelayedDecorator:
def __init__(i, cls, *args, **kwargs):
print("Delayed Decorator __init__", cls, args, kwargs)
i._cls = cls
i._args = args
i._kwargs = kwargs
def __call__(i, func):
print("Delayed Decorator __call__", func)
if not (callable(func)):
import io
with io.StringIO() as ss:
print(
"If only one input, input must be callable",
"Instead, received:",
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 schreiben wie:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
Beachten Sie, dass:
dec
akzeptiert nicht mehrere Argumente.
dec
akzeptiert nur die zu verpackende Funktion.
import inspect class PolyArgDecoratorMeta (Typ): def call (Wait, * args, ** kwargs): try: arg_count = len (args) if (arg_count == 1): if callable (args [0]): SuperClass = inspect. getmro (PolyArgDecoratorMeta) [1] r = SuperClass. Anruf (Wait, args [0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) endlich: pass return r
Zeitklasse importieren 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 zwei Codeteile sind äquivalent:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
Wir können "something"
sehr langsam wie folgt auf die Konsole drucken :
print_something("something")
#################################################
@Wait(delay=1)
def print_something_else(something_else):
print(something_else)
##################################################
def print_something_else(something_else):
print(something_else)
dd = DelayedDecorator(Wait, delay=1)
print_something_else = dd(print_something_else)
##################################################
print_something_else("something")
Schlussbemerkungen
Es mag wie eine Menge Code aussehen, aber Sie müssen nicht die Klassen schreiben DelayedDecorator
und PolyArgDecoratorMeta
jede Zeit. Der einzige Code, den Sie persönlich schreiben müssen, ist ziemlich kurz:
from PolyArgDecoratorMeta import PolyArgDecoratorMeta
import time
class Wait(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
execute_complete_reservation
nimmt zwei Parameter, aber Sie übergeben es einen. Dekorateure sind nur syntaktischer Zucker, um Funktionen in andere Funktionen zu verpacken. Eine vollständige Dokumentation finden Sie unter docs.python.org/reference/compound_stmts.html#function .