python - tight_layout - subplots_adjust
Décorateurs avec des paramètres? (4)
Je suppose que votre problème est de passer des arguments à votre décorateur. C'est un peu difficile et pas simple.
Voici un exemple de comment faire ceci:
class MyDec(object):
def __init__(self,flag):
self.flag = flag
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
print 'in decorator before wrapee with flag ',decorator_self.flag
original_func(*args,**kwargs)
print 'in decorator after wrapee with flag ',decorator_self.flag
return wrappee
@MyDec('foo de fa fa')
def bar(a,b,c):
print 'in bar',a,b,c
bar('x','y','z')
Impressions:
in decorator before wrapee with flag foo de fa fa
in bar x y z
in decorator after wrapee with flag foo de fa fa
J'ai un problème avec le transfert de la variable 'insurance_mode' par le décorateur. Je le ferais par la déclaration de décorateur suivante:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
mais malheureusement, cette déclaration ne fonctionne pas. Peut-être qu'il y a peut-être une meilleure façon de résoudre ce problème.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
Même si cette question a déjà été répondue et acceptée, je voudrais montrer une idée qui est à mon humble avis tout à fait élégante. La solution proposée par t.dubrownik montre un motif qui est toujours le même: vous avez besoin du wrapper à trois couches indépendamment de ce que fait le décorateur.
J'ai donc pensé que c'était un travail pour un méta-décorateur, c'est un décorateur pour les décorateurs. Comme un décorateur est une fonction, il fonctionne comme un décorateur régulier avec des arguments:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
Cela peut être appliqué à un décorateur régulier afin d'ajouter des paramètres. Par exemple, disons que nous avons le décorateur qui double le résultat d'une fonction:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print function(3) # Prints 26, namely 2 * (10 + 3)
Avec @parametrized
nous pouvons construire un décorateur @multiply
générique ayant un paramètre
@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print function(3) # Prints 26
@multiply(3)
def function_again(a):
return 10 + a
print function(3) # Keeps printing 26
print function_again(3) # Prints 39, namely 3 * (10 + 3)
Classiquement, le premier paramètre d'un décorateur paramétré est la fonction, tandis que les arguments restants correspondront au paramètre du décorateur paramétré.
Un exemple d'utilisation intéressant pourrait être un décorateur assertif de type:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Value %d has not type %s. %s instead' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 is str, arg2 is int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # prints hellohellohello
print(string_multiply(3, 3)) # Fails miserably with TypeError
Une note finale: ici, je n'utilise pas functools.wraps
pour les fonctions wrapper, mais je recommande de l'utiliser tout le temps.
Une façon de penser aux décorateurs avec des arguments est
@decorator
def foo(*args, **kwargs):
pass
Se traduit par
foo = decorator(foo)
Donc, si le décorateur avait des arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
Se traduit par
foo = decorator_with_args(arg)(foo)
decorator_with_args
est une fonction qui accepte un argument personnalisé et qui renvoie le décorateur lui-même (qui sera appliqué à la fonction décorée).
J'utilise un truc simple avec des partiels pour rendre mes décorateurs faciles
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
Voici une version légèrement modifiée de la réponse de t.dubrownik . Pourquoi? 1) En tant que modèle général, vous devez renvoyer la valeur de retour de la fonction d'origine. 2) Ceci change le nom de la fonction, ce qui pourrait affecter d'autres décorateurs / code. Donc, utilisez functools @wraps:
from functools import wraps
def decorator(argument):
def real_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
retval = function(*args, **kwargs)
more_funny_stuff()
return retval
return wrapper
return real_decorator