描述符
1.描述符本质就是一个新式类,在这个类中,至少实现了__set__,__get__,__delete__三种方法中的一种,也被称为描述符协议
__get__:调用相关属性时触发
__set__:修改相关属性时触发
__delete__:删除相关属性时触发
2.至少实现了__get__,和__set__的描述符称为数据描述符
不具有__set__方法的称为非数据描述符
3.属性调用优先级:类属性>数据描述符>实例属性>非数据描述符
4.描述符必须定义为类属性,不可放到函数中
5.描述符是用来代理另一个类的属性的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Foo: #self:描述符对象n, instance:实例b1 owner:所有者类,b1所属的类Bar def __get__(self, instance, owner): print('===>get方法',self,instance,owner) def __set__(self, instance, value): print('===>set方法',self,instance,value) # instance.__dict__['n']=value def __delete__(self, instance): print('===>delete方法',instance) class Bar: n=Foo() def __init__(self,name): self.name=name # Bar.n=10 #类属性的设置优先级高于描述符,不触发__set__ # print(Bar.n) #10 b1=Bar('alex') #不涉及属性n时不会触发描述符 b1.n #实例调用属性会触发__get__ #===>get方法 <__main__.Foo object at 0x00B3FFB0> <__main__.Bar object at 0x00B3F950> <class '__main__.Bar'> #???为什么字典属性里并没有‘n' : __set__方法并没有给字典添加 b1.n=10 #实例增加属性n,触发__set__,但类属性n不变 #===>set方法 <__main__.Foo object at 0x00B3FFB0> <__main__.Bar object at 0x00B3F950> 10 print(b1.__dict__) #{'name': 'alex'} print(b1.n) #触发__get__ # ===>get方法 <__main__.Foo object at 0x02EDFFB0> <__main__.Bar object at 0x02EDF950> <class '__main__.Bar'> # None del b1.n #触发__delete__ # ===>delete方法 <__main__.Bar object at 0x010EF950> del Bar.n #类删除属性也不会触发__delete__
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Foo: def __get__(self, instance, owner): print('===>get方法') def __set__(self, instance, value): print('===>set方法',instance,value) instance.__dict__['x']=value def __delete__(self, instance): print('===>delete方法') class Bar: x=Foo() #在何地? def __init__(self,n): self.x=n #b1.x=10 b1=Bar(10) print(b1.__dict__) b1.x=11111111111111111 print(b1.__dict__) b1.y=11111111111111111111111111111111111111 print(b1.__dict__) # result # ===>set方法 <__main__.Bar object at 0x02E0F950> 10 # {'x': 10} # ===>set方法 <__main__.Bar object at 0x02E0F950> 11111111111111111 # {'x': 11111111111111111} # {'x': 11111111111111111, 'y': 11111111111111111111111111111111111111}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Foo: def __get__(self, instance, owner): print('===>get方法') def __set__(self, instance, value): print('===>set方法',instance,value) # instance.__dict__['x']=value #b1.__dict__ def __delete__(self, instance): print('===>delete方法') class Bar: x=Foo() #在何地? print(Bar.x) #类调用属性时触发__get__ Bar.x=1 #类设置属性时不会触发__set__ print(Bar.__dict__) # {'__module__': '__main__', 'x': 1, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None} print(Bar.x) #1 ,这时就不会触发__get__ # b1=Bar() # b1.x #get # b1.x=1 # set # del b1.x # delete # b1=Bar() # Bar.x=111111111111111111111111111111111111111 # b1.x #不会触发__get__,类属性已经把描述符对象x覆盖了 # b1=Bar() # del Bar.x #不会触发__delete__ # b1.x #报错:AttributeError: 'Bar' object has no attribute 'x' # #---------------------------------------------------- #实例属性>非数据描述符 class Foo: def __get__(self, instance, owner): print('===>get方法') # def __delete__(self, instance): # print('===>delete方法') class Bar: x=Foo() #在何地? def __getattr__(self, item): print('----->') # b1=Bar() b1.x=1 print(b1.__dict__) #{'x': 1} b1.xxxxxxxxxxxxxxxxxxxxxxx #----->
5.描述符的应用:
python是一门弱类型语言,即参数的赋值没有类型限制,下面通过描述符机制来实现类型限制功能
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Typed: def __get__(self, instance, owner): print('get方法',instance,owner,sep='----') def __set__(self, instance, value): print('set方法',instance,value,sep='----') class People: name=Typed() def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary #name被代理了,self.name=name触发的是__set__方法,与self.age,salary触发的方法不同 p1=People('alex',18,66.6) print(p1.__dict__) p1.name p1.name='sanxi' print(p1.__dict__) #result # set方法----<__main__.People object at 0x00BCDAB0>----alex # {'age': 18, 'salary': 66.6} # get方法----<__main__.People object at 0x00BCDAB0>----<class '__main__.People'> # set方法----<__main__.People object at 0x00BCDAB0>----sanxi # {'age': 18, 'salary': 66.6}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Typed: def __init__(self,key): self.key=key def __get__(self, instance, owner): print('get方法',instance,owner,sep='----') def __set__(self, instance, value): print('set方法',instance,value,sep='----') # instance.__dict__['name']=value #写死了 if not isinstance(value,str): raise TypeError('输入类型不是字符串') instance.__dict__[self.key]=value def __delete__(self, instance): print('delete方法',instance) instance.__dict__.pop(self.key) class People: name=Typed('name') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('alex',18,66.6) #set方法 # p1.name #get方法 # print(p1.__dict__) #{'name': 'alex', 'age': 18, 'salary': 66.6} # p1.name='sanxi' #set方法 # print(p1.__dict__) #{'name': 'sanxi', 'age': 18, 'salary': 66.6} # # del p1.name #delete方法 # print(p1.__dict__) #{'age': 18, 'salary': 66.6} p1.name=123 #result: # Traceback (most recent call last): # set方法----<__main__.People object at 0x00FADAB0>----alex # set方法----<__main__.People object at 0x00FADAB0>----123 # File "D:/Programs/Python/Python37-32/untitled1/day21/time.py", line 37, in <module> # p1.name=123 # File "D:/Programs/Python/Python37-32/untitled1/day21/time.py", line 12, in __set__ # raise TypeError('输入类型不是字符串') # TypeError: 输入类型不是字符串
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Typed: def __init__(self,key,expected_type): self.key=key self.expected_type=expected_type def __get__(self, instance, owner): print('get方法',instance,owner,sep='----') def __set__(self, instance, value): print('set方法',instance,value,sep='----') # instance.__dict__['name']=value #写死了 if not isinstance(value,self.expected_type): raise TypeError('输入类型不是%s'%self.expected_type) instance.__dict__[self.key]=value def __delete__(self, instance): print('delete方法',instance) instance.__dict__.pop(self.key) class People: name=Typed('name',str) age=Typed('age',int) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('alex',18,66.6) #set方法 #result # set方法----<__main__.People object at 0x007E4130>----alex # set方法----<__main__.People object at 0x007E4130>----18
property:静态属性, 让你以为你是在调用一个数据属性,其实是在运行一个方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Lazyproperty: def __init__(self,func): print('==>',func) self.func=func def __get__(self, instance, owner): return self.func(instance) class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length # @property #area=property(area) @Lazyproperty def area(self): return self.width*self.length r1=Room('三夕',10,10) print(r1.area) #result: # ==> <function Room.area at 0x00A13108> # 100
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Lazyproperty: def __init__(self,func): print('==>',func) self.func=func def __get__(self, instance, owner): print(self,instance,owner,sep='---') if instance is None: return self return self.func(instance) class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length # @property #area=property(area) @Lazyproperty def area(self): return self.width*self.length print(Room.area) #result: # ==> <function Room.area at 0x00F32108> # <__main__.Lazyproperty object at 0x00E1F950>---None---<class '__main__.Room'> # <__main__.Lazyproperty object at 0x00E1F950>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Lazyproperty: def __init__(self,func): # print('==>',func) self.func=func def __get__(self, instance, owner): # print(self,instance,owner,sep='---') print('执行get方法') if instance is None: return self res=self.func(instance) setattr(instance,self.func.__name__,res) return res class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length # @property #area=property(area) @Lazyproperty def area(self): return self.width*self.length @property def area1(self): return self.width * self.length r1=Room('三夕',10,10) print(r1.area) print(r1.__dict__) #实例属性> 非数据描述符,所以再次调用area属性时,会去找实例属性,而不触发__get__方法,从而延迟计算 print(r1.area) #result # 执行get方法 # 100 # {'name': '三夕', 'width': 10, 'length': 10, 'area': 100} # 100