什么是描述符,Python描述符详解

Python中 , 通过使用描述符 , 可以让程序员在引用一个对象属性时自定义要完成的工作 。

什么是描述符,Python描述符详解

文章插图
 
本质上看 , 描述符就是一个类 , 只不过它定义了另一个类中属性的访问方式 。换句话说 , 一个类可以将属性管理全权委托给描述符类 。
描述符是Python中复杂属性访问的基础 , 它在内部被用于实现property、方法、类方法、静态方法和super类型 。
描述符类基于以下3个特殊方法 , 换句话说 , 这3个方法组成了描述符协议:
__set__(self,obj,type=None):在设置属性时将调用这一方法(本节后续用setter表示);
__get__(self,obj,value):在读取属性时将调用这一方法(本节后续用getter表示);
__delete__(self,obj):对属性调用del时将调用这一方法 。
其中 , 实现了setter和getter方法的描述符类被称为数据描述符;反之 , 如果只实现了getter方法 , 则称为非数据描述符 。
实际上 , 在每次查找属性时 , 描述符协议中的方法都由类对象的特殊方法__getattribute__()调用(注意不要和__getattr__()弄混) 。也就是说 , 每次使用类对象.属性(或者getattr(类对象 , 属性值))的调用方式时 , 都会隐式地调用__getattribute__() , 它会按照下列顺序查找该属性:
1、验证该属性是否为类实例对象的数据描述符;
2、如果不是 , 就查看该属性是否能在类实例对象的__dict__中找到;
3、最后 , 查看该属性是否为类实例对象的非数据描述符 。
为了表达清楚 , 这里举个例子:
【什么是描述符,Python描述符详解】#描述符类class revealAccess:def __init__(self, initval = None, name = 'var'):self.val = initvalself.name = namedef __get__(self, obj, objtype):print("Retrieving",self.name)return self.valdef __set__(self, obj, val):print("updating",self.name)self.val = valclass myClass:x = revealAccess(10,'var "x"')y = 5m = myClass()print(m.x)m.x = 20print(m.x)print(m.y)12345678910111213141516171819复制代码类型:[python]运行结果为:
Retrieving var "x"10updating var "x"Retrieving var "x"205123456复制代码类型:[python]从这个例子可以看到 , 如果一个类的某个属性有数据描述符 , 那么每次查找这个属性时 , 都会调用描述符的__get__()方法 , 并返回它的值;同样 , 每次在对该属性赋值时 , 也会调用__set__()方法 。
注意 , 虽然上面例子中没有使用__del__()方法 , 但也很容易理解 , 当每次使用del类对象.属性(或者delattr(类对象 , 属性))语句时 , 都会调用该方法 。
除了使用描述符类自定义类属性被调用时做的操作外 , 还可以使用property()函数或者@property装饰器 , 它们会在后续章节做详细介绍 。




    推荐阅读