Python各式裝飾器
Python裝飾器,分兩部分,一是裝飾器本身的定義,一是被裝飾器對象的定義。
一、函數式裝飾器:裝飾器本身是一個函數。
1.裝飾函數:被裝飾對象是一個函數
[1]裝飾器無參數:
a.被裝飾對象無參數:
>>> 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' >>></pre>
b.被裝飾對象有參數:
>>> 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' >>></pre>
[2]裝飾器有參數:
a.被裝飾對象無參數:
>>> 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) >>></pre>
由上面這段代碼中的最后兩個例子可知:當裝飾器有參數時,即使你啟用裝飾器的默認參數,不另外傳遞新值進去,也必須有一對括號,否則編譯器會直接將func傳遞給test(),而不是傳遞給_test()
b.被裝飾對象有參數:
>>> 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 >>></pre>
2.裝飾類:被裝飾的對象是一個類
[1]裝飾器無參數:
a.被裝飾對象無參數:
>>> 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 >>></pre>
b.被裝飾對象有參數:
>>> 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' >>></pre>
[2]裝飾器有參數:
a.被裝飾對象無參數:
>>> 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(). >>></pre>
b.被裝飾對象有參數:
>>> 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(). >>></pre>
二、類式裝飾器:裝飾器本身是一個類,借用
init()和call__()來實現職能 </p>1.裝飾函數:被裝飾對象是一個函數
[1]裝飾器無參數:
a.被裝飾對象無參數:
>>> 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' >>></pre>
b.被裝飾對象有參數:
>>> 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' >>></pre>
[2]裝飾器有參數
a.被裝飾對象無參數:
>>> 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' >>></pre>
或者:
>>> 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' >>></pre>
b.被裝飾對象有參數:
>>> 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' >>></pre>
或者:
>>> 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' >>></pre>
2.裝飾類:被裝飾對象是一個類
[1]裝飾器無參數:
a.被裝飾對象無參數:
>>> 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 >>></pre>
b.被裝飾對象有參數:
>>> 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' >>></pre>
[2]裝飾器有參數:
a.被裝飾對象無參數:
>>> 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 >>></pre>
b.被裝飾對象有參數:
>>> 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' >>></pre>
總結:【1】@decorator后面不帶括號時(也即裝飾器無參數時),效果就相當于先定義func或cls,而后執行賦值操作func=decorator(func)或cls=decorator(cls);
【2】@decorator后面帶括號時(也即裝飾器有參數時),效果就相當于先定義func或cls,而后執行賦值操作 func=decorator(decoratorArgs)(func)或cls=decorator(decoratorArgs)(cls);
【3】如上將func或cls重新賦值后,此時的func或cls也不再是原來定義時的func或cls,而是一個可執行體,你只需要傳入參數就可調用,func(args)=>返回值或者輸出,cls(args)=>object of cls;
【4】最后通過賦值返回的執行體是多樣的,可以是閉包,也可以是外部函數;當被裝飾的是一個類時,還可以是類內部方法,函數;
【5】另外要想真正了解裝飾器,一定要了解func.func_code.co_varnames,func.func_defaults,func.func_argcount,通過它們你可以以func的定義之外,還原func的參數列表,詳見Python多重裝飾器中的最后一個例子中的ArgsType;另外關鍵字參數是因為調用而出現的,而不是因為func的定義,func的定義中的用等號連接的只是有默認值的參數,它們并