属性描述符和属性查找过程
属性描述符是一个类,只需实现下面方法中的一种该类就可以被称之为属性描述符
1 class Person: 2 def __get__(self, instance, owner): 3 pass 4 5 def __set__(self, instance, value): 6 pass 7 8 def __delete__(self, instance): 9 pass
只需实现上面三个魔法函数的一种就可以称该类为属性描述符
property类实现了完整的描述符协议,通常可以只实现部分协议,
描述符的作用是:创建一个实例,作为另一个类的类属性
在下面的代码实例中,IntField是一个属性描述符,因为它都实现了这三种魔法方法,然后定义一个User类,里面的类变量是IntField属性描述符对象,当定义u.a = 30时,会调用__set__方法,从下面的图片中可以看到,self就是属性描述符对象,instance是User对象,value就是所附的值,这个有一种应用:就是判断输入的属性值是不是满足要求,可以用属性描述符来判断,在__set__函数里面加上判断,
1 class IntField: 2 def __get__(self, instance, owner): 3 pass 4 5 def __set__(self, instance, value): 6 pass 7 8 def __delete__(self, instance): 9 pass 10 11 12 class User: 13 a = IntField() 14 15 16 if __name__ == "__main__": 17 u = User() 18 u.a = 30
下面的例子就是用__set__做判断用户的输入
1 import numbers 2 3 4 class IntField: 5 def __get__(self, instance, owner): 6 pass 7 8 def __set__(self, instance, value): 9 if not isinstance(value, numbers.Integral): 10 raise ValueError("Int value need") 11 else: 12 self.value = value 13 14 def __delete__(self, instance): 15 pass 16 17 18 class User: 19 a = IntField() 20 21 22 if __name__ == "__main__": 23 u = User() 24 u.a = "abc"
从上面代码可以看出,现在赋值u.a = "abc",输出如下,可以看到功能成功实现。
1 import numbers 2 3 4 class IntField: 5 def __get__(self, instance, owner): 6 return 40 7 8 def __set__(self, instance, value): 9 if not isinstance(value, numbers.Integral): 10 raise ValueError("Int value need") 11 else: 12 self.value = value 13 14 def __delete__(self, instance): 15 pass 16 17 18 class User: 19 a = IntField() 20 21 22 if __name__ == "__main__": 23 u = User() 24 u.a = 30 25 print(u.a)
当想要获取对象的属性(属性描述符对象)的值时,它会调用__get__方法,如上所示,__get__方法返回的是40,当获取u.a的值时,会调用__get__方法返回40
数据属性描述符:实现__get__和__set__方法
非数据属性描述符:只实现__get__方法
如果user是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(obj)对象的__dict__中,那么直接返回obj.__dict__['age'],否则
(3)如果"age"出现在User或其基类__dict__中
(3.1) 如果age是non-data descriptor(非数据属性描述符),那么调用其__get__方法,否则
(3.2)返回__dict__['age']
(4) 如果User有__getattr__方法,调用__getattr__方法,否则
(5)抛出AttributeError