Details of various decorators in Python

  • 2020-05-07 19:58:19
  • OfStack

Python decorator, divided into two parts, 1 is the definition of the decorator itself, 1 is the definition of the object to be decorated.

Functional decorator: the decorator itself is a function.

1. Decoration function: the object to be decorated is a function

[1] decorator no parameters:

a. The decorated object has no parameters:


>>> def test(func):
    def _test():
        print 'Call the function %s().'%func.func_name
        return func()
    return _test >>> @test
def say():return 'hello world' >>> say()
Call the function say().
'hello world'
>>>

b. The decorated object has parameters:


>>> def test(func):
    def _test(*args,**kw):
        print 'Call the function %s().'%func.func_name
        return func(*args,**kw)
    return _test >>> @test
def left(Str,Len):
    #The parameters of _test can be '(Str,Len)' in this case.
    return Str[:Len] >>> left('hello world',5)
Call the function left().
'hello'
>>>

The [2] decorator has parameters:

a. The decorated object has no parameters:


>>> def test(printResult=False):
    def _test(func):
        def __test():
            print 'Call the function %s().'%func.func_name
            if printResult:
                print func()
            else:
                return func()
        return __test
    return _test >>> @test(True)
def say():return 'hello world' >>> say()
Call the function say().
hello world
>>> @test(False)
def say():return 'hello world' >>> say()
Call the function say().
'hello world'
>>> @test()
def say():return 'hello world' >>> say()
Call the function say().
'hello world'
>>> @test
def say():return 'hello world' >>> say() Traceback (most recent call last):
  File "<pyshell#224>", line 1, in <module>
    say()
TypeError: _test() takes exactly 1 argument (0 given)
>>>

From the last two examples in the above code: when the decorator has parameters, even if you enable the default parameters of the decorator and do not pass in the new value, you must have 1 pair of parentheses, otherwise the compiler will directly pass func to test() instead of _test()

b. The decorated object has parameters:


>>> def test(printResult=False):
    def _test(func):
        def __test(*args,**kw):
            print 'Call the function %s().'%func.func_name
            if printResult:
                print func(*args,**kw)
            else:
                return func(*args,**kw)
        return __test
    return _test >>> @test()
def left(Str,Len):
    #The parameters of __test can be '(Str,Len)' in this case.
    return Str[:Len] >>> left('hello world',5)
Call the function left().
'hello'
>>> @test(True)
def left(Str,Len):
    #The parameters of __test can be '(Str,Len)' in this case.
    return Str[:Len] >>> left('hello world',5)
Call the function left().
hello
>>>

 
2. Decoration class: the object to be decorated is a class

[1] decorator without parameters:

a. Decorated object has no parameters:


>>> def test(cls):
    def _test():
        clsName=re.findall('(\w+)',repr(cls))[-1]
        print 'Call %s.__init().'%clsName
        return cls()
    return _test >>> @test
class sy(object):
    value=32    
>>> s=sy()
Call sy.__init().
>>> s
<__main__.sy object at 0x0000000002C3E390>
>>> s.value
32
>>>

b. The decorated object has parameters:

>>> def test(cls):
    def _test(*args,**kw):
        clsName=re.findall('(\w+)',repr(cls))[-1]
        print 'Call %s.__init().'%clsName
        return cls(*args,**kw)
    return _test >>> @test
class sy(object):
    def __init__(self,value):
                #The parameters of _test can be '(value)' in this case.
        self.value=value        
>>> s=sy('hello world')
Call sy.__init().
>>> s
<__main__.sy object at 0x0000000003AF7748>
>>> s.value
'hello world'
>>>

  [2] decorator has parameters:

a. The decorated object has no parameters:


>>> def test(printValue=True):
    def _test(cls):
        def __test():
            clsName=re.findall('(\w+)',repr(cls))[-1]
            print 'Call %s.__init().'%clsName
            obj=cls()
            if printValue:
                print 'value = %r'%obj.value
            return obj
        return __test
    return _test >>> @test()
class sy(object):
    def __init__(self):
        self.value=32        
>>> s=sy()
Call sy.__init().
value = 32
>>> @test(False)
class sy(object):
    def __init__(self):
        self.value=32        
>>> s=sy()
Call sy.__init().
>>>

  b. The decorated object has parameters:
 


 >>> def test(printValue=True):
    def _test(cls):
        def __test(*args,**kw):
            clsName=re.findall('(\w+)',repr(cls))[-1]
            print 'Call %s.__init().'%clsName
            obj=cls(*args,**kw)
            if printValue:
                print 'value = %r'%obj.value
            return obj
        return __test
    return _test >>> @test()
class sy(object):
    def __init__(self,value):
        self.value=value        
>>> s=sy('hello world')
Call sy.__init().
value = 'hello world'
>>> @test(False)
class sy(object):
    def __init__(self,value):
        self.value=value        
>>> s=sy('hello world')
Call sy.__init().
>>>
 


Class decorator: the decorator itself is a class, using s 74en__ () and s 75en__ () to function

1. Decoration function: the object to be decorated is a function

[1] decorator without parameters:

a. The decorated object has no parameters:


>>> class test(object):
    def __init__(self,func):
        self._func=func
    def __call__(self):
        return self._func()    
>>> @test
def say():
    return 'hello world' >>> say()
'hello world'
>>>

b. The decorated object has parameters:


>>> class test(object):
    def __init__(self,func):
        self._func=func
    def __call__(self,*args,**kw):
        return self._func(*args,**kw)    
>>> @test
def left(Str,Len):
    #The parameters of __call__ can be '(self,Str,Len)' in this case.
    return Str[:Len] >>> left('hello world',5)
'hello'
>>>

The   [2] decorator has parameters

a. The decorated object has no parameters:


>>> class test(object):
    def __init__(self,beforeinfo='Call function'):
        self.beforeInfo=beforeinfo
    def __call__(self,func):
        def _call():
            print self.beforeInfo
            return func()
        return _call    
>>> @test()
def say():
    return 'hello world' >>> say()
Call function
'hello world'
>>>

Or:


 >>> class test(object):
    def __init__(self,beforeinfo='Call function'):
        self.beforeInfo=beforeinfo
    def __call__(self,func):
        self._func=func
        return self._call
    def _call(self):
        print self.beforeInfo
        return self._func()    
>>> @test()
def say():
    return 'hello world' >>> say()
Call function
'hello world'
>>>

  b. The decorated object has parameters:
 


 >>> class test(object):
    def __init__(self,beforeinfo='Call function'):
        self.beforeInfo=beforeinfo
    def __call__(self,func):
        def _call(*args,**kw):
            print self.beforeInfo
            return func(*args,**kw)
        return _call    
>>> @test()
def left(Str,Len):
    #The parameters of _call can be '(Str,Len)' in this case.
    return Str[:Len] >>> left('hello world',5)
Call function
'hello'
>>>
 

  or:
 


 >>> class test(object):
    def __init__(self,beforeinfo='Call function'):
        self.beforeInfo=beforeinfo
    def __call__(self,func):
        self._func=func
        return self._call
    def _call(self,*args,**kw):
        print self.beforeInfo
        return self._func(*args,**kw)    
>>> @test()
def left(Str,Len):
    #The parameters of _call can be '(self,Str,Len)' in this case.
    return Str[:Len] >>> left('hello world',5)
Call function
'hello'
>>>
 

  2. Decorator: the decorated object is one class

[1] decorator without parameters:

a. The decorated object has no parameters:


>>> class test(object):
    def __init__(self,cls):
        self._cls=cls
    def __call__(self):
        return self._cls()    
>>> @test
class sy(object):
    def __init__(self):
        self.value=32    
>>> s=sy()
>>> s
<__main__.sy object at 0x0000000003AAFA20>
>>> s.value
32
>>>

  b. The decorated object has parameters:
 


 >>> class test(object):
    def __init__(self,cls):
        self._cls=cls
    def __call__(self,*args,**kw):
        return self._cls(*args,**kw)    
>>> @test
class sy(object):
    def __init__(self,value):
        #The parameters of __call__ can be '(self,value)' in this case.
        self.value=value        
>>> s=sy('hello world')
>>> s
<__main__.sy object at 0x0000000003AAFA20>
>>> s.value
'hello world'
>>>
 

  [2] decorator has parameters:

a. The decorated object has no parameters:


>>> class test(object):
    def __init__(self,printValue=False):
        self._printValue=printValue
    def __call__(self,cls):
        def _call():
            obj=cls()
            if self._printValue:
                print 'value = %r'%obj.value
            return obj
        return _call    
>>> @test(True)
class sy(object):
    def __init__(self):
        self.value=32        
>>> s=sy()
value = 32
>>> s
<__main__.sy object at 0x0000000003AB50B8>
>>> s.value
32
>>>

  b. The decorated object has parameters:
 


 >>> class test(object):
    def __init__(self,printValue=False):
        self._printValue=printValue
    def __call__(self,cls):
        def _call(*args,**kw):
            obj=cls(*args,**kw)
            if self._printValue:
                print 'value = %r'%obj.value
            return obj
        return _call    
>>> @test(True)
class sy(object):
    def __init__(self,value):
        #The parameters of _call can be '(value)' in this case.
        self.value=value        
>>> s=sy('hello world')
value = 'hello world'
>>> s
<__main__.sy object at 0x0000000003AB5588>
>>> s.value
'hello world'
>>>
 

Summary: [1] @decorator with no parentheses (i.e., no arguments for the decorator), the effect is equivalent to defining func or cls, and then performing the assignment operation func=decorator(func) or cls=decorator(cls);

[2] when @decorator is followed by parentheses (that is, when the decorator has arguments), the effect is equivalent to defining func or cls, and then performing the assignment func=decorator(decoratorArgs)(func) or cls=decorator(decoratorArgs)(cls);

[3] if func or cls is reassigned, func or cls is no longer func or cls, but an executable. You only need to pass in the parameters to call func(args)= > Return value or output, cls(args)= > object of cls;

[4] finally, the execution body returned by the assignment is various, which can be a closure or an external function. When decorated with a class, it can also be a class internal method, function;

[5] to really understand decorators, 1 must understand func.func_code.co_varnames, func.func_defaults, through which you can restore func's parameter list in addition to func's definition; In addition, keyword parameters appear because of the call, not because of the definition of func. The parameters in the definition of func are only those with default values that are concatenated with equals. They do not necessarily become keyword parameters because you can still pass them by location.


Related articles: