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__ 中找到;
- 最後,查看該屬性是否為類實例對象的非數據描述符。