属性描述符--利用的是抽象的方法, 把十几个字段共同的特性抽出来,每个字段都用这个特性,达到节省代码的目的。
属性描述符,有数据属性描述符和非数据属性描述符
__get__
__set__
__delete__
实现上述三个魔法函数其中之一即可成为属性描述符,如果只实现__get__称之为非数据属性描述符,只有同时实现__get__和__set__才称之为数据属性描述符
1 #!/user/bin/env python 2 # -*- coding:utf-8 -*- 3 import numbers 4 5 6 class IntField: 7 def __get__(self, instance, owner): 8 return self.value 9 10 def __set__(self, instance, value): 11 if not isinstance(value, numbers.Integral): 12 raise ValueError('int value need') 13 if value < 0: 14 raise ValueError('positive value need') 15 self.value = value 16 17 def __delete__(self, instance): 18 pass 19 20 21 class User: 22 age = IntField() 23 24 25 if __name__ == '__main__': 26 user = User() 27 user.age = 30 28 print(user.age) 29 30 # ValueError('int value need') 31 # user.age = 'z' 32 # print(user.age) 33 34 # ValueError('positive value need') 35 # user.age = -1 36 # print(user.age)
30
总结一下:
1.需要把校验放在一个类里面(IntField)
2.需要重写魔法函数
3.age = IntField()
4.给age赋值的时候,会调用IntField里面的__get__方法
属性查找过程
如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’)),首先调用__getattribute__。如果类定义了__getattr__方法,那么在__getattribute__抛出 AttributeError 的之前就会调用__getattr__,而对于描述符(__get__)的调用,则是发生在__getattribute__内部的。
user = User(), 那么user.age 顺序如下:
(1)如果“age”是出现在User类或其基类的__dict__中(类属性), 且age是data descriptor, 那么调用其__get__方法, 否则
(2)如果“age”出现在user的__dict__中(对象属性), 那么直接返回 obj.__dict__['age'], 否则 -->如果age不是类属性或者User不是数据属性描述符,则去对象属性中查找age
(3)如果“age”出现在User或其基类的__dict__中(类属性)
(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则
(3.2)返回 class.__dict__['age']
(4)如果User有__getattr__方法,调用__getattr__方法,否则 -->对象属性和类属性中都没有age,则调用User类的__getattr__方法
(5)抛出AttributeError
(3.1)
1 #!/user/bin/env python 2 # -*- coding:utf-8 -*- 3 import numbers 4 5 6 class IntField: 7 def __get__(self, instance, owner): 8 return self.value 9 10 def __set__(self, instance, value): 11 if not isinstance(value, numbers.Integral): 12 raise ValueError('int value need') 13 if value < 0: 14 raise ValueError('positive value need') 15 self.value = value 16 17 def __delete__(self, instance): 18 pass 19 20 21 class NotDataIntField: 22 def __get__(self, instance, owner): 23 return self.value 24 25 26 class User: 27 age = NotDataIntField() 28 29 30 if __name__ == '__main__': 31 user = User() 32 User.age = 30 33 User.z = 10 34 print(User.__dict__) 35 print(user.__dict__) 36 print(User.age) 37 print(user.age) 38 39 # ValueError('int value need') 40 # user.age = 'z' 41 # print(user.age) 42 43 # ValueError('positive value need') 44 # user.age = -1 45 # print(user.age)
{'__module__': '__main__', 'age': 30, '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None, 'z': 10} {} 30 30