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 @
trabajó. Desafortunadamente, esa no es la forma en que se hacen las cosas.
Considere un decorador Wait
que interrumpe 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)
, luego la llamada Wait(3)
es ejecutado antes de pasa cualquier otra cosa.
Es decir, los siguientes dos fragmentos 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 por crear 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 que se va a envolver.importar inspeccionar clase PolyArgDecoratorMeta (tipo): def llama(Espere, * args, ** kwargs): intente: arg_count = len (args) if (arg_count == 1): if in invocable (args[0]): SuperClass = inspeccionar.getmro (PolyArgDecoratorMeta)[1]
r = Superclase.llama(Espera, argumentos[0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) finalmente: pass return rclase de tiempo de importación Wait (metaclass = PolyArgDecoratorMeta): def en eso(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
Los siguientes dos fragmentos 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"
a 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 es necesario que escriba las clases. DelayedDecorator
y PolyArgDecoratorMeta
cada vez. El único código que tiene para escribir personalmente 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