Decorators with parameters?

前端 未结 13 1657
我在风中等你
我在风中等你 2020-11-21 22:58

I have a problem with the transfer of variable \'insurance_mode\' by the decorator. I would do it by the following decorator statement:

@execute_complete_rese         


        
13条回答
  •  傲寒
    傲寒 (楼主)
    2020-11-21 23:28

    I'd like to show an idea which is IMHO quite elegant. The solution proposed by t.dubrownik shows a pattern which is always the same: you need the three-layered wrapper regardless of what the decorator does.

    So I thought this is a job for a meta-decorator, that is, a decorator for decorators. As a decorator is a function, it actually works as a regular decorator with arguments:

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

    This can be applied to a regular decorator in order to add parameters. So for instance, say we have the decorator which doubles the result of a function:

    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)
    

    With @parametrized we can build a generic @multiply decorator having a parameter

    @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)
    

    Conventionally the first parameter of a parametrized decorator is the function, while the remaining arguments will correspond to the parameter of the parametrized decorator.

    An interesting usage example could be a type-safe assertive decorator:

    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
    

    A final note: here I'm not using functools.wraps for the wrapper functions, but I would recommend using it all the times.

提交回复
热议问题