摘要.
一句话解释: 描述符是一个用于维护单一属性的类
描述符2个部分:
1.描述符介绍
2.描述符实际应用
描述符是一个独立的类.
特性(property,在另一篇文章中已经介绍并讲解2种方式)是描述符的一种.
描述符用于对属性的拦截 (获取/修改(设置)/ 删除)
具体来说,描述符的作用在于, 对某个特定的属性在获取/删除/设置 方面拦截了一下,
给与用户自己来定义的一个过程
切记切记,描述符实例是用于赋给一个类属性的,在下面讲解
_
一个简单的描述符:
1. class Descriptor:
2. def __get__(self, instance, owner):pass
3. def __set__(self, instance, value):pass
4. def __delete__(self, instance):pass
含有任何3个方法其中的一种就可以看作是一个描述符.
get : 获取, set :设置/修改 , __delete:删除
1. #描述符
2. #用于单一属性的获取,设置/修改 , 删除
3. #是一个类
4. #类中含有__get__, __set__ , __delete__ 方法的可以看作一个描述符
5. class D: #创建一个类, 添加了一个__get__ 方法
6. def __get__(self, instance, owner): #参数: 自身实例, 所附加的客户类实例,附加到的类
7. #3个参数无需手动传递
8. print(self,instance,owner,sep='\t')
9.
10. class Sub:
11. attr = D() #添加了一个属性, Sub.attr , 赋值为一个 描述符
12. s = Sub()
13. s.attr #当获取attr时. 由于attr是一个描述符 => D.__get__(Sub.attr, s, Sub)
14. Sub.attr
15. #echo:
16. #<__main__.D object at 0x00608DF0> <__main__.Sub object at 0x00608ED0> <class '__main__.Sub'>
17. #<__main__.D object at 0x00608DF0> None <class '__main__.Sub'>
1. #描述符的坑 , 与python的赋值相关
2. s.attr = '123' #你想为原attr 赋值吗? 实际是s 实例新增了一个 attr 属性
3. print(s.__dict__)
当描述符含有__set__ 方法时, 实例将不再使用常规的层级赋值行为
1. #给描述符赋值 ,绕过常规的爬属性: 在描述符类中添加一个__set__
2. class D:
3. def __get__(self, instance, owner):
4. print(D.__get__.__name__ , ' invoked')
5. def __set__(self, instance, value):
6. print(D.__set__.__name__, ' invoked' , ',value:' ,value)
7. #raise AttributeError("不能设置") 如果想attr 属性只读 , 可以在set中抛出异常
8. class C:
9. attr = D()
10.
11. c = C()
12. c.attr = 1 #当描述符中含有__set__ 可以赋值 ,展开表达式: D.__set__(C.attr,c,C)
接下来一个完整的实际的例子
1. #描述符对象
2. #一个描述符对象维护一个属性的获取,设置/修改,删除
3. #切记切记,描述符实例是用于赋给一个类属性的. 如果赋给实例属性,就无法工作了
4. class Name:
5. "描述符文档"
6. def __get__(self, instance, owner): #取值
7. print(Name.__get__.__name__, ' invoked!')
8. return instance._name
9. def __set__(self, instance, value): #赋值
10. print(Name.__set__.__name__, ' invoked!')
11. instance._name = value
12. def __delete__(self, instance):
13. print(Name.__delete__.__name__ , " invoked!") #删除属性
14. del instance._name
15.
16. class Person:
17. def __init__(self , name = "Person"):
18. self._name = name
19. name = Name() #当实例使用 name 属性时,将触发描述符对象的使用
20.
21. p = Person()
22. print(p.name) #触发 Name.__get__(Person.name,p , Person)
23. p.name = 'hi' #触发 Name.__set__(Person.name,p,'hi')
24. del p.name #触发 Name.__delete__(Person.name,p)
25. #echo
26. #__get__ invoked!
27. #Person
28. #__set__ invoked!
29. #__delete__ invoked!
1. #同样的如果一个描述类只作用于 某个特殊的类 , 可以使用嵌套类 来实现
2. class Person:
3. def __init__(self,value = 'Person'):
4. self._name = value
5. #把描述符类写在内部 , 其他没变
6. class DescriptorName:
7. def __get__(self, instance, owner):
8. print(self.__class__.__name__,' __get__')
9. return instance._name
10. def __set__(self, instance, value):
11. print(self.__class__.__name__,'__set__')
12. instance._name = value
13. def __delete__(self, instance):
14. print(self.__class__.__name__,'__delete__')
15. del instance._name
16. name = DescriptorName() #产生一个描述符对象
17. p1 = Person()
18. p1.name
当要把一个属性特殊化时,可以考虑使用描述符,这样当使用这个属性时,实际是使用了描述符对象来进行处理;
下面一些具体的例子, 给描述符类添加__init__ , 描述符也是一个普通的类,也可以添加属性
1. #描述符对象
2. class DescState:
3. #添加一个init , 来初始化描述符自身的属性value
4. def __init__(self,value):
5. self.value = value
6. def __get__(self, instance, owner):
7. print(DescState.__name__,DescState.__get__.__name__,'invoked!',sep='\t')
8. return self.value
9. def __set__(self, instance, value):
10. print(DescState.__name__,DescState.__get__.__name__, ' invoked!',sep='\t')
11. self.value = value
12.
13. #描述符对象
14. #假设被附加到的类实例有个属性_name
15. class InstState:
16. def __get__(self, instance, owner):
17. print(InstState.__name__,'__get__ invoked!')
18. return instance._name #假设实例有个属性: _name
19. def __set__(self, instance, value):
20. print(InstState.__name__,'__set__ invoked!')
21. instance._name = value
1. #一个客户类
2. #attr_desc 自身拥有一个属性
3. #attr_inst 将给实例本身的属性:_name 赋值,取值
4. #实例本身拥有2个属性, _name, _instAttr
5. class ClientClass:
6. attr_desc = DescState(10) #创建一个描述符对象,并赋予初始值,这个描述符对象有自己的属性
7. attr_inst = InstState() #创建一个描述符对象,这个描述符对象将修改与返回实例的属性:_name
8. def __init__(self,name = 'ClientClass'):
9. self._name = name #2个实例属性
10. self._instAttr = 1
11.
12. obj = ClientClass()
13. #使用3个不同的属性,分别输出
14. #表达式展开: 1.DescState.__get__() 2.InstState.__get__()
15. print(obj.attr_desc ,obj.attr_inst,obj._instAttr)
16. #echo:
17. #DescState __get__ invoked!
18. #InstState __get__ invoked!
19. #10 ClientClass 1
20.
21. obj.attr_desc = 5 #DescState.__set__()
22. obj.attr_inst = 6 #InstState.__set__()
23. obj._instAttr = 7
24. print(obj.attr_desc ,obj.attr_inst,obj._instAttr)
25. #echo:
26. #DescState __set__ invoked!
27. #InstState __set__ invoked!
28. #DescState __get__ invoked!
29. #InstState __get__ invoked!
30. #5 6 7