Python中的描述符

一、什么是描述符?

  简单的说,首先要有一个实现了__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,解释器也不会有任何异常。当然可以想到解决办法,就是提供一个gettersetter方法来统一读写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逻辑编写单独的类来处理。

 

  

 

 

posted @ 2020-07-19 16:41  vadie  阅读(243)  评论(0编辑  收藏  举报