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__(),它會按照下列順序查找該屬性:

  1. 驗證該屬性是否為類實例對象的數據描述符;
  2. 如果不是,就查看該屬性是否能在類實例對象的 __dict__ 中找到;
  3. 最後,查看該屬性是否為類實例對象的非數據描述符。