描述符的本质就是新式类,其至少实现了__get__(),__set__(),__delete__()中的一个,也被称为描述符协议;
__get__():在调用一个属性时,触发
__set__():在为一个属性赋值时,触发
__delete__():在用del删除属性时,触发
描述符的作用:用来代理另一个类的类属性(必须是类属性,不能在类的函数构造中),且描述符在自身调用,赋值,删除属性时不会被触发
class Str:
def __get__(self, instance, owner):
print('触发Strget',instance,owner)
def __set__(self, instance, value):
print('触发Strset',instance,value)
def __delete__(self, instance):
print('触发Strdel')
class Int:
def __get__(self, instance, owner):
print('触发Intget',instance,owner)
def __set__(self, instance, value):
print('触发Intset',instance,value)
def __delete__(self, instance):
print('触发Intdel')
class People:
name=Str()
age=Int()
def __init__(self,name,age):
self.name=name
self.age=age
#自身的实例不会触发
s=Str()
s.x=1
del s.x
i=Int()
i.y=2
del i.y
#实例化people时触发
p=People('alex',18)
#描述符Str的使用
p.name
p.name='Bart'
del p.name
#描述符Int的使用
p.age
p.age=19
del p.age
print(p.__dict__)#为空,因为name和age属性被Str和Int代理,在Str和Int中处理,并没有写入p.__dict__中
print(People.__dict__)
描述符分两种一 数据描述符:
1.数据描述符:至少实现了__get__()和__set__()
2. 非数据描述符:没有实现__set__()
注意:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()
class Str:
def __get__(self, instance, owner):
print('触发Strget',instance,owner)
def __set__(self, instance, value):
print('触发Strset',instance,value)
def __delete__(self, instance):
print('触发Strdel')
class People:
name=Str()
def __init__(self,name):
self.name=name
#类属性>数据描述符>实例属性
#数据符大于实例属性
p=People('Alex')#触发了__set__
p.name #触发了__get__
p.name='Bart' #触发了__set__
del p.name #触发了__delete__
print(p.__dict__)
#类属性大于数据描述符
print('====>',People.__dict__['name'])
People.name='Bart'#不会触发__set__,直接People.__dict__中name对应的值由__set__的内存地址改为Bart
print('====>',People.__dict__['name'])
print(p.name) #不再触发__get__
class Str:
def __get__(self, instance, owner):
print('触发Strget',instance,owner)
# def __set__(self, instance, value):
# print('触发Strset',instance,value)
# def __delete__(self, instance):
# print('触发Strdel')
class People:
name=Str()
def __init__(self,name):
self.name=name
#实例大于非数据描述符
p=People('Alex')
p.name
p.name='Bart'
#都不会触发
描述符的作用
描述符可以规定输入的值的类型
class Type:
def __init__(self,key,type):
self.key=key
self.type=type
def __get__(self, instance, owner):
print('触发get')
return instance.__dict__[self.key]
def __set__(self, instance, value):
print('触发set')
if not isinstance(value,self.type):
raise TypeError('%s不是%s'%(value,self.type))
else:
instance.__dict__[self.key]=value
def __delete__(self, instance):
print('触发del')
class People:
name=Type('name',str)
age=Type('age',int)
salary=Type('salary',float)
def __init__(self,name,age,salary):
self.name=name
self.age=age
self.salary=salary
p1=People("alex",18,3000.0)
print(p1.__dict__)
p2=People('bart','18',3000)