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

Voir l'article de Bruce Eckel pour plus de détails.

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




decorator