Python/描述符
< Python
描述符(descriptor)是一种类。把实现了__get__()、__set__()和__delete__()中的其中任意一种方法的类称之为描述符。这3种方法称为描述符协议。
描述符的作用是用来构造出类的一个属性(property)。它只属于类的,不属于实例。描述符是Python类特性中最底层的数据结构的实现手段。经常被使用的@classmethod、@staticmethd、@property、甚至是__slots__等属性都是通过描述符来实现的。它是很多高级库和框架的重要工具之一。是使用到装饰器或者元类的大型框架中的一个非常重要组件。
使用描述符,可以让程序员在引用一个对象属性时自定义要完成的工作。本质上看,描述符就是一个类,只不过它定义了另一个类中属性的访问方式。换句话说,一个类可以将属性管理全权委托给描述符类。
如下示例一个描述符及引用描述符类的代码。Descriptors类就是一个描述符,Person是使用描述符的类:
class Descriptors:
def __init__(self, key, value_type):
self.key = key
self.value_type = value_type
def __get__(self, instance, owner):
print("执行Descriptors的get")
return instance.__dict__[self.key]
def __set__(self, instance, value):
print("执行Descriptors的set")
if not isinstance(value, self.value_type):
raise TypeError("参数%s必须为%s"%(self.key, self.value_type))
instance.__dict__[self.key] = value
def __delete__(self, instance):
print("执行Descriptors的delete")
instance.__dict__.pop(self.key)
class Person:
name = Descriptors("name", str)
age = Descriptors("age", int)
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("xiaoming", 15)
print(person.__dict__)
person.name
person.name = "jone"
print(person.__dict__)
#输出:
#执行Descriptors的set
#执行Descriptors的set
#{'name': 'xiaoming', 'age': 15}
#执行Descriptors的get
#执行Descriptors的set
#{'name': 'jone', 'age': 15}
- 至少实现了内置__set__()和__get__()方法的描述符称为数据描述符;
- 实现了除__set__()以外的方法的描述符称为非数据描述符。
描述符的优先级的高低顺序:类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()。所以,用className.VarName=value,则为类属性;用instanceName.VarName=value,则优先是描述符。
在每次查找属性时,描述符协议中的方法都由类对象的特殊方法 __getattribute__() 调用(注意不要和 __getattr__() 弄混)。也就是说,每次使用InstanceName.VarName(或者 getattr(InstanceName, VarName))的调用方式时,都会隐式地调用 __getattribute__(),它会按照下列顺序查找该属性:
- 验证该属性是否为类实例对象的数据描述符;
- 如果不是,就查看该属性是否能在类实例对象的 __dict__ 中找到;
- 最后,查看该属性是否为类实例对象的非数据描述符。