python描述符与实例属性_Python属性描述符与属性查找过程

什么是属性描述符

# 某个类实现 __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