01
背景
很多Python库中都会出现描述器(descriptor)的身影,Python内置的staticmethod, classmethod, property,super
等方法也都是描述器。了解描述器协议对于我们理解这些对象的行为会有很大帮助。
02
定义
通常,描述符是具有描述符协议中方法之一的属性值。这些方法是get(),set()和delete()。如果为属性定义了那些方法中的任何一种,则称其为描述符。
如果一个对象定义了以下三个方法中的任意一个,那么这个对象就可以被称之为描述器:
__set__(**)
__get__(**)
__delete__(**)
03
分类
如果对象定义set()或delete(),则将其视为数据描述符。仅定义get()的描述符称为非数据描述符(它们通常用于方法,但也可以用于其他用途)。
描述器又可分为数据描述器(data descriptor)和非数据描述器(non-data descriptor),只有*get*
方法的对象被称为非数据描述器(non-data desriptor),其他的都称为数据描述器(data descriptor)。
04
使用
下面通过一个例子来展示描述器是怎么使用的。
class PositiveNumber(object):
def __init__(self, name, value):
self.name = name
self._value = value
def __get__(self, obj, dtype):
return self._value
def __set__(self, obj, value):
if value < 0:
raise ValueError('Cannot be negative.')
self._value = value
if __name__ == '__main__':
number = PositiveNumber('number', 10)
上述代码中的number便是一个描述器。一般来说,描述器是作为另一个对象的属性来出现的:
class PositiveNumber(object):
def __init__(self, name, value):
self.name = name
self._value = value
def __get__(self, obj, dtype):
print("call `__get__` method")
print("obj : {}".format(obj))
print("dtype: {}".format(dtype))
return self._value
def __set__(self, obj, value):
if value < 0:
raise ValueError('Cannot be negative.')
print("call `__set__` method")
print("obj : {}".format(obj))
print("set price to {:d}".format(value))
self._value = value
def __delete__(self, obj):
print("call `__delete__` method")
print("obj : {}".format(obj))
print("delete object :{}".format(obj))
class Apple(object):
price = PositiveNumber('price', 0)
def __init__(self, price):
self.price = price
这样,描述器number作为Apple类的一个属性,在创建新的Apple实例apple=Apple(10)的时候,self.price = 10便会触发描述器的__set__方法, 监测price参数是否为正数,并在不符合条件的时候引发异常。
同样,当我们执行print(apple.price)时,dot操作符会触发描述器的__get__方法,执行删除操作的时候会触发描述器的__delete__方法。
这样,我们就通过定义描述器的方式,改变了对象的属性在赋值、删除和访问时的默认行为,描述器的功能正在于此。
05
扩展
1.Python内置的staticmethod, classmethod, property,super
等方法也都是描述器,具体的时候可以查看相应的文档
2.实例和类的dotted lookup 优先级有所不同,所以描述器在被类和实例调用时的行为也略有不同,参见Python文档。
参考资料
https://docs.python.org/zh-cn/3/howto/descriptor.html
https://zhuanlan.zhihu.com/p/52708890
往期推荐