Es bien sabido que las siguientes dos piezas de código son casi equivalentes:
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
Un error común es pensar que @
simplemente oculta el argumento más a la izquierda.
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
Sería mucho más fácil escribir decoradores si lo anterior es cómo @
funcionó. Desafortunadamente, esa no es la forma en que se hacen las cosas.
Considere un decorador Wait
que frena la ejecución del programa durante unos segundos. Si no pasa un tiempo de espera, el valor predeterminado es 1 segundo. Los casos de uso se muestran a continuación.
##################################################
@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)
Cuando Wait
tiene un argumento, como @Wait(3)
, entonces la llamada Wait(3)
se ejecuta antes que suceda algo más.
Es decir, las siguientes dos piezas de código son equivalentes
@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)
Esto es un problema.
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
A continuación se muestra una solución:
Comencemos creando la siguiente clase 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)
Ahora podemos escribir cosas como:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
Tenga en cuenta que:
dec
no acepta múltiples argumentos.
dec
solo acepta la función a envolver.
importar inspeccionar clase PolyArgDecoratorMeta (type): def call (Wait, * args, ** kwargs): try: arg_count = len (args) if (arg_count == 1): if callable (args [0]): SuperClass = inspect. getmro (PolyArgDecoratorMeta) [1] r = Superclase. llamada (Wait, args [0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) finalmente: pasar retorno r
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
Las siguientes dos piezas de código son equivalentes:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
Podemos imprimir "something"
en la consola muy lentamente, de la siguiente manera:
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")
Notas finales
Puede parecer mucho código, pero no tiene que escribir las clases DelayedDecorator
y PolyArgDecoratorMeta
todo el tiempo. El único código que tiene que escribir personalmente es algo como lo siguiente, que es bastante corto:
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
toma dos parámetros, pero lo está pasando uno. Los decoradores son simplemente azúcar sintáctica para envolver funciones dentro de otras funciones. Consulte docs.python.org/reference/compound_stmts.html#function para obtener la documentación completa.