É sabido que os dois seguintes códigos são quase equivalentes:
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
Um erro comum é pensar que @
simplesmente oculta o argumento mais à esquerda.
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
Seria muito mais fácil escrever decoradores se o que precede é como @
funcionou. Infelizmente, não é assim que as coisas são feitas.
Considere um decorador Wait
que interrompe a execução do programa por alguns segundos. Se você não passar um tempo de espera, o valor padrão será 1 segundo. Os casos de uso são mostrados abaixo.
##################################################
@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)
Quando Wait
tem um argumento, como @Wait(3)
, então, a chamada Wait(3)
é executada antes que qualquer outra coisa aconteça.
Ou seja, os dois seguintes códigos são 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)
Isto é um 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
Uma solução é mostrada abaixo:
Vamos começar criando a seguinte classe 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)
Agora podemos escrever coisas como:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
Observe que:
dec
não aceita vários argumentos.
dec
aceita apenas a função a ser quebrada.
importar inspecione a classe PolyArgDecoratorMeta (tipo): def call (Aguarde, * args, ** kwargs): tente: arg_count = len (args) if (arg_count == 1): se for possível chamar (args [0]): SuperClass = inspecionar. getmro (PolyArgDecoratorMeta) [1] r = SuperClasse. ligar (Aguarde, args [0]) else: r = DelayedDecorator (espera, * args, ** kwargs) else: r = DelayedDecorator (espera, * args, ** kwargs) finalmente: passa retorno r
classe de tempo de importação 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
Os dois seguintes pedaços de código são equivalentes:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
Podemos imprimir "something"
no console muito lentamente, da seguinte maneira:
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 Finais
Pode parecer muito código, mas você não precisa escrever as classes DelayedDecorator
e PolyArgDecoratorMeta
sempre. O único código que você precisa escrever pessoalmente é o seguinte, que é bastante curto:
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
requer dois parâmetros, mas você está passando um. Decoradores são apenas açúcar sintático para envolver funções dentro de outras funções. Consulte docs.python.org/reference/compound_stmts.html#function para obter a documentação completa.