一、描述符是什么
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
2 def __get__(self, instance, owner):
3 print('__get__(),被执行了')
4 def __set__(self, instance, value):
5 print('__set__(),被执行了')
6 def __delete__(self, instance):
7 print('__delete__(),被执行了')
二、描述符的作用
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
1 class Test:
2 x = Foo()
3 def __init__(self,x):
4 self.x = x
5
6 t = Test(2) #'__set__(),被执行了'
7 print(t.x) #'__get__(),被执行了' 'None'
三、描述符分为两种
1、数据描述符:至少实现了__get__()和__set__()
2、非数据描述符:没有实现__set__()
1 #数据描述符
2 class Foo:
3 def __set__(self, instance, value):
4 print('set')
5 def __get__(self, instance, owner):
6 print('get')
7
8 #非数据描述符
9 class Foo:
10 def __get__(self, instance, owner):
11 print('get')
四、注意事项:
描述符本身应该定义成新式类,被代理的类也应该是新式类
必须把描述符定义成这个类的类属性,不能为定义到构造函数中
要严格遵循该优先级,优先级由高到底分别是
1类属性
2数据描述符
3实例属性
4非数据描述符
5找不到的属性触发__getattr__()
五、描述符的应用
1、现在有一个需求,定义一个用户信息,用户的名字为字符串类型,年龄为int类型,收入为float类型,可以用描述符来代理这些属性,
从而控制传入的数据类型。
1 # 定义描述符
2 class Desc_type:
3 def __get__(self, instance, owner):
4 print('执行了__get__')
5 print('instance是 %s'%instance) #instance表示的是被代理的类属性的类实例化出的对象,这里是p1
6 print('owner是 %s'%owner) #owner表示的是被代理的类,这里是People这个类
7 def __set__(self, instance, value):
8 print('执行了__set__')
9 print('instance是 %s'%instance) #instance表示的是被代理的类属性的类实例化出的对象,这里是p1
10 print('value是 %s'%value) #value表示的是被代理的类的属性的值,这里是'Menawey'
11 print('self=====%s'%self) #self表示的是描述符实例的对象Desc_type()--->name
12 def __delete__(self, instance):
13 print('执行了__delete__')
14 print('instance是 %s' % instance)
15
16 # 定义一个人的类(被代理的类)
17 class People:
18 name = Desc_type() #用描述符代理了name这个属性
19 def __init__(self,name,age,salary):
20 self.name = name
21 self.age = age
22 self.salary = salary
23
24 p1 = People('Meanwey',24,11.1)
25 print(p1.name) #会出发__get__
26 print(p1.__dict__) #{'age': 24, 'salary': 11.1}
发现被代理的name属性并没有被设置对应的值,所以__dict__中没有'name',那是因为实例化的时候执行了__init__,所以也执行了__set__,
但是在__set__中并没有真正的操作进行设置
2、所以要想真正的对属性进行代理,对属性进行设置、获取和删除值,则需要通过操作底层__dict__字典,如下:
1 # 定义描述符
2 class Desc_type:
3 def __init__(self,key,value_type): #传入key用来操作底层属性字典,value_type用来表示期望的数据类型
4 self.key = key
5 self.value_type = value_type
6 def __get__(self, instance, owner):
7 print('执行了__get__')
8 return instance.__dict__[self.key] #return p2.name
9 def __set__(self, instance, value):
10 print('执行了__set__',self)
11 if not isinstance(value,str): #用来判断用户传入的是否符合要求
12 raise TypeError('%s 传入的不是 %s'%(self.key,self.value_type)) #抛出类型异常,提示用户程序终止
13 instance.__dict__[self.key] = value #符合要求,则设置属性对应的值
14 def __delete__(self, instance):
15 print('执行了__delete__')
16 instance.__dict__.pop(self.key)
17
18 # 定义一个人的类(被代理的类)
19 class People:
20 name = Desc_type('name',str) # 用描述符代理了name这个属性,相当于执行了Desc_type中的self.__set__
21 age = Desc_type('age',int)
22 salary = Desc_type('salary',float)
23 def __init__(self, name, age, salary):
24 self.name = name
25 self.age = age
26 self.salary = salary
27 p2 = People('Meanwey',24,11.1)
28
29 #访问
30 print(p2.name)
31
32 #赋值
33 p2.name = 'Jery'
34 p2.age = 18.1 #没有传入整型的数据,则 ----TypeError: age 传入的不是 <class 'int'>
六、总结
1、描述符就是一个类(新式类);
2、描述符分为数据描述和非数据描述符,区别在于前者有__set__方法,后者没有;
3、描述符的使用要遵循优先级:类属性>数据描述符>实例属性>非数据描述符>找不到(__getattr__);
4、描述符方法中的self表示的是描述符实例化的对象,instance表示的是被描述(代理)的类实例化的对象,owner表示的是被描述(代理)的类,value表示的是设置到被描述(代理)属性的值。