裝飾是為函數(shù)和類指定管理代碼的一種方式.裝飾器本身的形式是處理其他的可調(diào)用對象的可調(diào)用的對象。
- 函數(shù)裝飾器在函數(shù)定義的時候進行名稱重綁定,提供一個邏輯層來管理函數(shù)和方法或隨后對他們的調(diào)用
- 類裝飾器在類定義的時候進行名稱重綁定,提供一個邏輯層來管理類,或管理隨后調(diào)用他們所創(chuàng)建的示例
基礎(chǔ)知識
@decorator
def F(arg):pass
F(99) #調(diào)用函數(shù)
def F(arg):pass
F = decorator(F)
F(99) #調(diào)用函數(shù)
這一自動名稱重綁定在def語句上有效,不管它針對一個簡單的函數(shù)或是類中的一個方法。當隨后調(diào)用F函數(shù)的時候,它自動調(diào)用裝飾器所返回的對象,該對象可能是實現(xiàn)了所需的包裝邏輯的另一個對象,或者是最初的函數(shù)本身。
裝飾器在裝飾的時候調(diào)用,并且當隨后調(diào)用最初的函數(shù)名的時候,它所返回的調(diào)用對象將被調(diào)用。裝飾器本身接收被裝飾的函數(shù),返回的調(diào)用對象會接收隨后傳遞給給裝飾函數(shù)的名稱的任何參數(shù)。
def decorator(F):
def wrapper(*args):
print(args)
return wrapper
@decorator
def func(x,y):
pass
if __name__ == '__main__':
func(6,7) #調(diào)用方法實際上調(diào)用的是wrapper(6,7)
#func = decorator(func) #func現(xiàn)在是decorator返回的包裝器函數(shù)wrapper
#func(6,7)
class decorator:
def \_\_init__(self,func):
self.func = func
def \_\_call__(self,*args):
print(args)
self.func(*args)
@decorator
def func(x,y):
print(x+y)
if __name__ == '__main__':
func(6,7)
###
現(xiàn)在,隨后在調(diào)用func的時候,它確實會調(diào)用裝飾器所創(chuàng)建的實例\__call__運算符重載方法;然后\__call__方法可能運行最初的func,因為它在一個實例屬性中仍然可用。當按照這種方式編寫代碼的時候,每個裝飾的函數(shù)都會產(chǎn)生一個新的實例來保持狀態(tài)
--------------------------------------------------------------------------
def decorator(F):
def wrapper(*args):
F(*args)
return wrapper
class C:
@decorator
def method(self,x,y):
print(x*y)
if __name__ == '__main__':
c = C()
c.method(6,7)
42
類裝飾器
基礎(chǔ)知識
@decorator
class C:
....
x = C(99)
相當于
class C:......
C = decorator(C)
x = C(99)
也是返回一個可調(diào)用對象的一個可調(diào)用對象,因此大多數(shù)函數(shù)和類的組合已經(jīng)足夠了
一個例子
#-*-coding:UTF-8-*-
def decorator(cls):
class Wrapper:
def __init__(self, *arg):
self.wrappered = cls(*arg)
def __getattr__(self,name):
return getattr(self.wrappered,name)
return Wrapper
@decorator
class C:
def __init__(self,x,y):
self.attr = 'spam'
if __name__ == '__main__':
x = C(6,7)
print(x.attr)
"""
這個例子中,裝飾器把C類的名稱重新綁定到另一個類(Wrapper),這個類(Wrapper)在一個封閉的作用域中保持了最初的類(C)
,并且當調(diào)用它的時候,創(chuàng)建并嵌入了最初的類的一個實例。
當隨后從該實例獲取一個屬性的時候,包裝器的__getattr__攔截了它,并且將其委托給最初的類的嵌入的實例
"""
裝飾器嵌套
語法
@A
@B
@C
def f(...):
...
相當于:
def f(...):....
f = A(B(C(f)))
例如:
def d1(F):return lambda:'X'+F()
def d2(F):return lambda:'Y'+F()
def d3(F):return lambda:'Z'+F()
@d1
@d2
@d3
def func():
return 'spam'
if __name__ == '__main__':
print(func())
>>>XYZspam
裝飾器參數(shù)
函數(shù)裝飾器和類裝飾器似乎都能接受參數(shù),盡管實際上這些參數(shù)傳遞給了真正返回裝飾器的一個可回調(diào)對象,而裝飾器反過來又返回一個可調(diào)用對象
語法
@decorator(A,B)
def F(arg):...
F(99)
自動映射到其對等的形式,其中裝飾器是一個可調(diào)用對象,它返回實際的裝飾器。返回的裝飾器反過來返回可調(diào)用的對象
def F(arg):
...
F = decorator(A,B)(F)
F(99)
跟蹤調(diào)用的例子
"""
這個類裝飾的每個函數(shù)將如何創(chuàng)建一個新的實例,帶有自己保存的函數(shù)對象和
調(diào)用計數(shù)器。還要注意觀察,*args參數(shù)語法如何來打包和解包任意的多個
傳入?yún)?shù)。這個類裝飾器可以用來包裝帶有任意多個參數(shù)的任何函數(shù)
"""
class tracer:
def __init__(self,func):
self.calls = 0
self.func = func #把被包裝的函數(shù)存儲為自己的實例屬性
def __call__(self,*args):
self.calls += 1
print('call %s to %s'%(self.calls,self.func.\__name__))
self.func(*args)
@tracer
def spam(a,b,c):
print(a+b+c)
if __name__ == '__main__':
spam(1,2,3)
>>>
call 1 to spam
6
###
類裝飾器tracer不適合裝飾其他類中的方法,例如
class Person:
"""docstring for Person"""
def __init__(self,name,pay):
self.name = name
self.pay= pay
@tracer
def giveRaise(self,percent):
self.pay*=(1.0+percent)
tracer類的call方法的self是一個tracer實例,沒有在參數(shù)列表中傳遞Person主體
使用嵌套函數(shù)來裝飾方法
如果想讓函數(shù)小黃時期在簡單函數(shù)和類方法上都能工作,最直接的解決方法在于把自己的函數(shù)裝飾器編寫成嵌套的def,以便對于包裝器類和主類實例都不依賴單個的self實例參數(shù)
#-*-coding:UTF-8-*-
def tracer(func):
calls = 0
def onCall(*args,**kwargs):
nonlocal calls
calls+=1
print('call %s to %s'%(calls,func.__name__))
return func(*args,**kwargs)
return onCall
@tracer
def spam(a,b,c):
print(a+b+c)
class Person:
def __init__(self,name,pay):
self.name = name
self.pay = pay
# @tracer
def giveRaise(self,percent):
self.pay *= (1.0+percent)
@tracer
def lastName(self):
return self.name.split()[-1]
if __name__ == '__main__':
spam(1,2,3)
bob = Person('Bob Smith',50000)
bob.giveRaise = tracer(bob.giveRaise) #裝飾器的原型
bob.giveRaise(.10)
print(int(bob.pay))
**添加裝飾器參數(shù)
#-*-coding:UTF-8-*-
import time
def timer(label='',trace=True):
class Timer:
def __init__(self,func):
self.func = func
self.alltime = 0
def __call__(self,*args,**kargs):
start = time.clock()
result = self.func(*args,**kargs)
elapsed = time.clock()-start
self.alltime += elapsed
if trace:
format = '%s %s:%.5f,%.5f'
values = (label,self.func.__name__,elapsed,self.alltime)
print(format%values)
return result
return Timer
@timer(label='[CCC]==>')
def listcomp(N):
return [x * 2 for x in range(N)]
if __name__ == '__main__':
print(listcomp(50))
編寫類裝飾器
def Tracer(aClass):
class Wrapper:
def __init__(self,*args,**kargs):
self.fetches = 0
self.wrapped = aClass(*args,**kargs) #實例化aClass類的一個實例
def __getattr__(self,attrname):
print('Trace: '+attrname)
self.fetches += 1
return getattr(self.wrapped,attrname) #調(diào)用self.wrapped的attrname方法(屬性)
return Wrapper
"""
類裝飾器 編寫為一個類的情況
class Tracer:
def __init__(self,aClass): #運行到@的時候會觸發(fā)\__init__
self.aClass = aClass
def __call__(self,*args): #下面實例化主類的時候,Spam()會調(diào)用\__call__。需要返回一個主類的對象
self.wrapped = self.aClass(*args)
return self
def __getattr__(self,attrname):
print('Trace: '+attrname)
return getattr(self.wrapped,attrname)
"""
@Tracer
class Spam:
def display(self,a): #添加一個參數(shù)
print('Spam '*8+a)
@Tracer
class Person:
def __init__(self,name,hours,rate):
self.name = name
self.hours = hours
self.rate = rate
def pay(self):
return self.hours * self.rate
if __name__ == '__main__':
food = Spam()
food.display('a') #必須提供一個參數(shù)
print(food.fetches)
bob = Person('Bob',40,50)
print(bob.name)
實現(xiàn)私有屬性
#-*-coding:UTF-8-*-
traceMe = False
def trace(*args):
if traceMe:print('['+' '.join(map(str,args))+']')
def Private(*privates):
print(privates)
def onDecorator(aClass):
class onInstance:
def __init__(self,*args,**kargs):
self.wrapped = aClass(*args,**kargs) #自己初始化是也會調(diào)用__setattr__
print(aClass.__name__)
def __getattr__(self,attr):
trace('get:',attr)
if attr in privates:
raise TypeError('private attr')
else:
return getattr(self.wrapped,attr)
def __setattr__(self,attr,value):
trace('set: ',attr,value)
print('--->',attr)
if attr == 'wrapped':
self.__dict__[attr] = value
elif attr in privates:
raise TypeError('private attr change')
else:
setattr(self.wrapped,attr,value)
return onInstance
return onDecorator
if __name__ == '__main__':
traceMe = True
@Private('data','size')
class Doubler:
def __init__(self,label,start):
self.label = label
self.data = start
def size(self):
return len(self.data)
def double(self):
for i in range(self.size()):
self.data[i] = self.data[i] * 2
def display(self):
print('%s=>%s'%(self.label,self.data))
"""
Doubler = Private('data','size')(Doubler)
Private('data','size') 返回的是onDecorator函數(shù),而此函數(shù)接收一個類
并在該函數(shù)返回一個包裝類onInstance,onInstance在封閉的onDecorator函數(shù)中
aClass是傳遞給onDecorator函數(shù)的參數(shù),可以在onInstance內(nèi)部使用aClass
"""
X = Doubler('X is',[1,2,3])
print(X.label)
X.display()
# X.data = [1,2,3,4]