Il est bien connu que les deux morceaux de code suivants sont presque équivalents:
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
Une erreur courante est de penser que @
cache simplement l'argument le plus à gauche.
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
Il serait beaucoup plus facile d'écrire des décorateurs si ce qui précède est comme cela a @
fonctionné. Malheureusement, ce n'est pas ainsi que les choses se font.
Considérons un décorateur Wait
qui interrompt l'exécution du programme pendant quelques secondes. Si vous ne passez pas de temps d'attente, la valeur par défaut est 1 seconde. Les cas d'utilisation sont indiqués ci-dessous.
##################################################
@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)
Quand Wait
a un argument, tel que @Wait(3)
, alors l'appel Wait(3)
est exécuté avant qu'il ne se passe quoi que ce soit d'autre.
Autrement dit, les deux morceaux de code suivants sont équivalents
@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)
C'est un problème.
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
Une solution est présentée ci-dessous:
Commençons par créer la classe suivante 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)
Maintenant, nous pouvons écrire des choses comme:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
Notez que:
dec
n'accepte pas plusieurs arguments.
dec
accepte uniquement la fonction à encapsuler.
import inspect classe 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 = SuperClass. call (Wait, args [0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) enfin: pass return r
importation de la classe de temps Wait (métaclasse = 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
Les deux morceaux de code suivants sont équivalents:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
Nous pouvons imprimer "something"
sur la console très lentement, comme suit:
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")
Notes finales
Cela peut ressembler à beaucoup de code, mais vous n'avez pas à écrire les classes DelayedDecorator
et à PolyArgDecoratorMeta
chaque fois. Le seul code que vous devez écrire personnellement comme suit, qui est assez court:
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
prend deux paramètres, mais vous en passez un. Les décorateurs ne sont que du sucre syntaxique pour encapsuler des fonctions à l'intérieur d'autres fonctions. Voir docs.python.org/reference/compound_stmts.html#function pour une documentation complète.