装饰器的一些基础知识:
类装饰的大坑有2种:
1.在作用于类方法的时候,被装饰的类实例无法把自身的self , 传递进装饰类中(__call__),除非使用描述符类装饰器
2.无法简单的保持多个实例 (见下面)
下面用过一个装饰器来重定向,这个函数装饰器可以产生多个实例.
之后的一个例子把下面的函数装饰器修改成类装饰器,将出错(大坑)
#装饰器(函数/类)通常用于装饰一个函数或方法
#*描述符装饰器能更好的支持一个类方法的装饰情况
#装饰整个类,用__getattr__ 来拦截一个类的调用次数
def tracer(aClass):
class Wrapper: #内部类
def __init__(self,*args,**kwargs):
self.callTimes = 0
self.wrapper = aClass(*args,**kwargs) #原类 , 封装在实例的属性中
def __getattr__(self, item):
print('__getattr__')
self.callTimes += 1
return getattr(self.wrapper,item)
return Wrapper
@tracer
class Person: #Person = tracer(Person) = > Person 现在是一个Wrapper类
def display(self):
print('display')
p = Person() #p 是一个Wrapper对象
p.display()
print(p)
#echo
#__getattr__
#display
#<__main__.tracer.<locals>.Wrapper object at 0x003E7D70>
类装饰的一些坑:把上面的装饰器修改一下
#现在把上面的例子修改一下成类装饰器, 这是类装饰器的坑!!
class Tracer:
def __init__(self,aClass): #接受一个类对象
self.aClass = aClass
self.wrapper = None #wrapper是在调用__call__之后产生的Person对象
def __call__(self, *args, **kwargs):
print('__call__ ,args:',args,kwargs)
self.wrapper = self.aClass(*args,**kwargs) #创建一个Person对象
return self
def __getattr__(self, item):
print('__getattr__')
return getattr(self.wrapper,item) #getattr 获取Person对象中的属性
@Tracer
class Person: #Person = Tracer(Person) , Person此时是一个Tracer对象
def __init__(self,name='person'):
self.name = name
def printname(self):
print(self.name)
x = Person('x')
y = Person('y')
x.printname() #出错了, 打印出来了y
#echo
#__call__ ,args: ('x',) {}
#__call__ ,args: ('y',) {}
#__getattr__
#y
这个是类装饰器的一个坑, 每当调用Person('xx')之后,Tracer会调用__call__,内部的wrapper就会产生一个对象,wrapper
只会保持最后一个对象的引用
如果要产生多个实例用函数装饰器来的更直接