Decorators with parameters?

前端 未结 13 1410
我在风中等你
我在风中等你 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:36

    It is well known that the following two pieces of code are nearly equivalent:

    @dec
    def foo():
        pass    foo = dec(foo)
    
    ############################################
    foo = dec(foo)
    

    A common mistake is to think that @ simply hides the leftmost argument.

    @dec(1, 2, 3)
    def foo():
        pass    
    ###########################################
    foo = dec(foo, 1, 2, 3)
    

    It would be much easier to write decorators if the above is how @ worked. Unfortunately, that’s not the way things are done.


    Consider a decorator Waitwhich haults program execution for a few seconds. If you don't pass in a Wait-time then the default value is 1 seconds. Use-cases are shown below.

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

    When Wait has an argument, such as @Wait(3), then the call Wait(3) is executed before anything else happens.

    That is, the following two pieces of code are equivalent

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

    This is a problem.

    if `Wait` has no arguments:
        `Wait` is the decorator.
    else: # `Wait` receives arguments
        `Wait` is not the decorator itself.
        Instead, `Wait` ***returns*** the decorator
    

    One solution is shown below:

    Let us begin by creating the following class, 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)
    

    Now we can write things like:

     dec = DelayedDecorator(Wait, delay=4)
     @dec
     def delayed_print(something):
        print(something)
    

    Note that:

    • dec does not not accept multiple arguments.
    • dec only accepts the function to be wrapped.

      import inspect class 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) finally: pass return r

      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 
      

    The following two pieces of code are equivalent:

    @Wait
    def print_something(something):
         print (something)
    
    ##################################################
    
    def print_something(something):
        print(something)
    print_something = Wait(print_something)
    

    We can print "something" to the console very slowly, as follows:

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

    Final Notes

    It may look like a lot of code, but you don't have to write the classes DelayedDecorator and PolyArgDecoratorMeta every-time. The only code you have to personally write something like as follows, which is fairly short:

    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
    

提交回复
热议问题