527 votes

décorateurs de Python avec paramètres

J'ai un problème avec le transfert de la variable 'insurance_mode' par le décorateur. Je le ferais par la déclaration suivante de décorateur:

  @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
 

892voto

t.dubrownik Points 1874

Tu veux dire def test_booking_gta_object, droite? De toute façon, la syntaxe pour les décorateurs avec des arguments est un peu différent - le décorateur avec des arguments doit retourner une fonction qui va prendre une fonction et retourner une autre fonction. Donc, il devrait vraiment revenir normal décorateur. Un peu déroutant, non? Ce que je veux dire, c'est:

def decorator(argument):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            funny_stuff()
            something_with_argument(argument)
            function(*args, **kwargs)
            more_funny_stuff()
        return wrapper
     return real_decorator

Ici vous pouvez lire plus sur le sujet - il est également possible de mettre en œuvre cette aide appelable objets et qui est également expliqué.

403voto

srj Points 399

Une façon de penser à des décorateurs avec des arguments est

@decorator
def foo(*args, **kwargs):
    pass

Se traduit par

foo = decorator(foo)

donc, si le décorateur avait arguments:

@decorator_with_args(arg)
def foo(*args, **kwargs):
    pass

se traduit par

foo = decorator_with_args(arg)(foo)

le décorateur devient alors une fonction qui accepte un argument, et retourne une fonction qui accepte une fonction qui renvoie une autre fonction.

Ignorer l'ancienne ligne si elle fait tourner la tête un peu :).

J'utilise un truc tout simple avec les harmoniques de faire mon décorateurs facile

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

108voto

Dacav Points 2536

Même si cette question a déjà été répondu et accepté, j'aimerais vous montrer une idée qui est à mon humble avis tout à fait élégante. La solution proposée par t.dubrownik montre un modèle qui est toujours le même: vous avez besoin de trois couches wrapper indépendamment de ce que le décorateur.

J'ai donc enseigné que c'est un travail pour une méta-décorateur, qui est un décorateur pour les décorateurs. Un décorateur est une fonction, il fonctionne en fait comme un régulier décorateur avec des arguments:

def parametrized(dec):
    def layer(*args, **kwargs):
        def repl(f):
            return dec(f, *args, **kwargs)
        return repl
    return layer

Ceci peut être appliqué à un régulière décorateur afin d'ajouter des paramètres. Ainsi, 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 générique @multiply décorateur d'avoir 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)    # Keeps printing 26

@multiply(3)
def function_again(a):
    return 10 + a

print function(3)    # Prints 39, namely 3 * (10 + 3)

Classiquement le premier paramètre d'une paramétrées, décorateur est la fonction, tandis que les autres arguments correspond au paramètre de la paramétrées, décorateur.

Un intéressant exemple d'utilisation pourrait être un type sûr autoritaire décorateur:

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

40voto

Ross Rogers Points 5619

Je présume que votre problème est en train de transmettre des arguments à votre décorateur. C'est un peu délicat et pas simple.

Voici un exemple de la façon de procéder:

 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X