装饰是为函数和类指定管理代码的一种方式.装饰器本身的形式是处理其他的可调用对象的可调用的对象。
- 函数装饰器在函数定义的时候进行名称重绑定,提供一个逻辑层来管理函数和方法或随后对他们的调用
- 类装饰器在类定义的时候进行名称重绑定,提供一个逻辑层来管理类,或管理随后调用他们所创建的示例
基础知识
@decorator
def F(arg):pass
F(99) #调用函数
def F(arg):pass
F = decorator(F)
F(99) #调用函数
这一自动名称重绑定在def语句上有效,不管它针对一个简单的函数或是类中的一个方法。当随后调用F函数的时候,它自动调用装饰器所返回的对象,该对象可能是实现了所需的包装逻辑的另一个对象,或者是最初的函数本身。
装饰器在装饰的时候调用,并且当随后调用最初的函数名的时候,它所返回的调用对象将被调用。装饰器本身接收被装饰的函数,返回的调用对象会接收随后传递给给装饰函数的名称的任何参数。
def decorator(F):
def wrapper(*args):
print(args)
return wrapper
@decorator
def func(x,y):
pass
if __name__ == '__main__':
func(6,7) #调用方法实际上调用的是wrapper(6,7)
#func = decorator(func) #func现在是decorator返回的包装器函数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)
###
现在,随后在调用func的时候,它确实会调用装饰器所创建的实例\__call__运算符重载方法;然后\__call__方法可能运行最初的func,因为它在一个实例属性中仍然可用。当按照这种方式编写代码的时候,每个装饰的函数都会产生一个新的实例来保持状态
--------------------------------------------------------------------------
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
类装饰器
基础知识
@decorator
class C:
....
x = C(99)
相当于
class C:......
C = decorator(C)
x = C(99)
也是返回一个可调用对象的一个可调用对象,因此大多数函数和类的组合已经足够了
一个例子
#-*-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)
,并且当调用它的时候,创建并嵌入了最初的类的一个实例。
当随后从该实例获取一个属性的时候,包装器的__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
装饰器参数
函数装饰器和类装饰器似乎都能接受参数,尽管实际上这些参数传递给了真正返回装饰器的一个可回调对象,而装饰器反过来又返回一个可调用对象
语法
@decorator(A,B)
def F(arg):...
F(99)
自动映射到其对等的形式,其中装饰器是一个可调用对象,它返回实际的装饰器。返回的装饰器反过来返回可调用的对象
def F(arg):
...
F = decorator(A,B)(F)
F(99)
跟踪调用的例子
"""
这个类装饰的每个函数将如何创建一个新的实例,带有自己保存的函数对象和
调用计数器。还要注意观察,*args参数语法如何来打包和解包任意的多个
传入参数。这个类装饰器可以用来包装带有任意多个参数的任何函数
"""
class tracer:
def __init__(self,func):
self.calls = 0
self.func = func #把被包装的函数存储为自己的实例属性
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实例,没有在参数列表中传递Person主体
使用嵌套函数来装饰方法
如果想让函数小黄时期在简单函数和类方法上都能工作,最直接的解决方法在于把自己的函数装饰器编写成嵌套的def,以便对于包装器类和主类实例都不依赖单个的self实例参数
#-*-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))
**添加装饰器参数
#-*-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) #调用self.wrapped的attrname方法(属性)
return Wrapper
"""
类装饰器 编写为一个类的情况
class Tracer:
def __init__(self,aClass): #运行到@的时候会触发\__init__
self.aClass = aClass
def __call__(self,*args): #下面实例化主类的时候,Spam()会调用\__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): #添加一个参数
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') #必须提供一个参数
print(food.fetches)
bob = Person('Bob',40,50)
print(bob.name)
实现私有属性
#-*-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) #自己初始化是也会调用__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函数,而此函数接收一个类
并在该函数返回一个包装类onInstance,onInstance在封闭的onDecorator函数中
aClass是传递给onDecorator函数的参数,可以在onInstance内部使用aClass
"""
X = Doubler('X is',[1,2,3])
print(X.label)
X.display()
# X.data = [1,2,3,4]