一、什么是描述符?
简单的说,首先要有一个实现了__get__()、__set__()、__delete__()中任意一种方法的新式类(Python 2.x版本默认旧式类,通过继承object为新式类),并且这个新式类的实例对象是另外一个类的属性,这个属性就被称之为描述符。
class MyDescriptor: def __get__(self, instance, owner): print(‘get called‘) return ‘get‘ def __set__(self, instance, value): print(‘set called‘) def __delete__(self, instance): print(‘delete called‘) class Foo: attr = MyDescriptor() # 描述符
上面代码中Foo的属性attr是类MyDescriptor的实例化对象,MyDescriptor实现了__get__()
、__set__()
和__delete__()
,那么attr就是成为了描述符,注意attr必须用类属性形式写。
二、描述符有什么用?
1.类型检查
由于Python
是一个动态类型解释性语言,不像C/C++
等静态编译型语言,数据类型在编译时便可以进行验证,而Python
中必须添加额外的类型检查逻辑代码才能做到这一点,这就是描述符的初衷。比如,有一个测试类Test
,其具有一个类属性name。
class Test(object): name = None
? 正常情况下,name
的值(其实应该是对象, name
是引用)都应该是字符串,但是因为Python
是动态类型语言,即使执行Test.name = 3
,解释器也不会有任何异常。当然可以想到解决办法,就是提供一个getter
,setter
方法来统一读写name
,读写前添加安全验证逻辑。
class Test(object): name = None @classmethod def get_name(cls): return cls.name @classmethod def set_name(cls, val): if isinstance(val, str): cls.name = val else: raise TypeError("Must be an string")
虽然以上代码勉强可以实现对属性赋值的类型检查,但是会导致类型定义的臃肿和逻辑的混乱,而描述符就恰好能解决这一问题。
为name
属性定义一个(数据)描述符类,其实现了__get__
和__set__
方法,代码如下:
class NameDes(object): def __init__(self): self.__name = None def __get__(self, instance, owner): print(‘call __get__‘) return self.__name def __set__(self, instance, value): print(‘call __set__‘) if isinstance(value,str): self.__name = value else: raise TypeError("Must be an string")
当实例对象访问name属性时,就会调用__get__方法,打印name属性所需要的值,当使用实例对象对name属性进行修改或赋值时,则会调用__set__方法,这时只需要在set方法添加所需限制条件,就可以限制name属性的类型,弥补了python动态类型的缺陷。
2.弥补property属性的缺陷
property属性和描述符有一个相似点,那就是都可以对属性进行限制操作(不了解的建议去补下property属性)
代码如下:
class Student(object): def __init__(self, hight): self._hight = hight # 单位cm @property def hight(self): return self._hight @hight.setter def hight(self, value): # 判断输入的类型 if not isinstance(value, int): raise TypeError # 判断是否符合逻辑 if value >= 300 or value < 0: raise ValueError # 进行修改 self._hight = value @hight.deleter def hight(self): del self._hight s01 = Student(175) print(s01.hight) # 175 s01.hight = 255 print(s01.hight) # 255 s01.hight = "hello" # TypeError 类型错误 del s01.hight print(s01.hight)
通过上述代码可以看出property属性也可以限制属性的修改赋值,但也发现,s01.hight看似调用的是hight这一实例属性,却是隐藏调用的getter/setter方法,看起来很臃肿。
对property
来说,最大的缺点就是它们不能重复使用。
如果只是对一个属性进行限制的话,那么property属性用起来也没有多大问题,但如果同时需要限制多个属性时,就需要多组getter/setter/deleter方法来实现,极大的降低了代码的复用性。
这就是描述符所解决的问题。描述符是property
的升级版,允许你为重复的property
逻辑编写单独的类来处理。
原文:https://www.cnblogs.com/vadie/p/13332825.html