什么是属性描述符
# 某个类实现 __get__(),__set__(),__delete__() 中任意一个就是一个属性描述符
class IntField:
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
属性描述符作用
修改 IntField
import numbers
class IntField:
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
# 判断 value 类型
if not isinstance(value, numbers.Integral):
raise ValueError("int value need")
self.value = value
def __delete__(self, instance):
pass
class User:
age = IntField()
if __name__ == "__main__":
user = User()
user.age = 30
print(user.age)
# 控制赋值行为
user.age = "abc"
Output:
ValueError: int value need
30
数据属性描述符合非数据属性描述符
class NonDataIntField:
# 非数据属性描述符
def __get__(self, instance, owner):
return self.value
描述符分为数据描述符和非数据描述符。把至少实现了内置属性__set__()和__get__()方法的描述符称为数据描述符;把实现了除__set__()以外的方法的描述符称为非数据描述符。之所以要区分描述符的种类,主要是因为它在代理类属性时有着严格的优先级限制。例如当使用数据描述符时,因为数据描述符大于实例属性,所以当我们实例化一个类并使用该实例属性时,该实例属性已被数据描述符代理,此时我们对该实例属性的操作是对描述符的操作。
属性查找顺序
如果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’], 否则
(3)如果“age”出现在User或其基类的__dict__中
(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则
(3.2)返回 __dict__[‘age’]
(4)如果User有__getattr__方法,调用__getattr__方法,否则
(5)抛出AttributeError