zoukankan      html  css  js  c++  java
  • Python进阶-----描述符(__get__(),__set__(),__delete__())

    一、描述符是什么
      描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
      __get__():调用一个属性时,触发
      __set__():为一个属性赋值时,触发
      __delete__():采用del删除属性时,触发

    1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
    2     def __get__(self, instance, owner):
    3         print('__get__(),被执行了')
    4     def __set__(self, instance, value):
    5         print('__set__(),被执行了')
    6     def __delete__(self, instance):
    7         print('__delete__(),被执行了')

    二、描述符的作用
      描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

    1 class Test:
    2     x = Foo()
    3     def __init__(self,x):
    4         self.x = x
    5 
    6 t = Test(2)      #'__set__(),被执行了'
    7 print(t.x)         #'__get__(),被执行了'   'None'

    三、描述符分为两种
      1、数据描述符:至少实现了__get__()和__set__()
      2、非数据描述符:没有实现__set__()

     1 #数据描述符
     2 class Foo:
     3     def __set__(self, instance, value):
     4         print('set')
     5     def __get__(self, instance, owner):
     6         print('get')
     7 
     8 #非数据描述符
     9 class Foo:
    10     def __get__(self, instance, owner):
    11         print('get')

    四、注意事项:
      描述符本身应该定义成新式类,被代理的类也应该是新式类
      必须把描述符定义成这个类的类属性,不能为定义到构造函数中
      要严格遵循该优先级,优先级由高到底分别是
        1类属性
        2数据描述符
        3实例属性
        4非数据描述符
        5找不到的属性触发__getattr__()

    五、描述符的应用
      1、现在有一个需求,定义一个用户信息,用户的名字为字符串类型,年龄为int类型,收入为float类型,可以用描述符来代理这些属性,
    从而控制传入的数据类型。

     1 # 定义描述符
     2 class Desc_type:
     3     def __get__(self, instance, owner):
     4         print('执行了__get__')
     5         print('instance是 %s'%instance)            #instance表示的是被代理的类属性的类实例化出的对象,这里是p1
     6         print('owner是 %s'%owner)                #owner表示的是被代理的类,这里是People这个类
     7     def __set__(self, instance, value):
     8         print('执行了__set__')
     9         print('instance是 %s'%instance)            #instance表示的是被代理的类属性的类实例化出的对象,这里是p1
    10         print('value是 %s'%value)                #value表示的是被代理的类的属性的值,这里是'Menawey'
    11         print('self=====%s'%self)               #self表示的是描述符实例的对象Desc_type()--->name
    12     def __delete__(self, instance):
    13         print('执行了__delete__')
    14         print('instance是 %s' % instance)
    15 
    16 # 定义一个人的类(被代理的类)
    17 class People:
    18     name = Desc_type()            #用描述符代理了name这个属性
    19     def __init__(self,name,age,salary):
    20         self.name = name
    21         self.age = age
    22         self.salary = salary
    23 
    24 p1 = People('Meanwey',24,11.1)
    25 print(p1.name)                    #会出发__get__
    26 print(p1.__dict__)                 #{'age': 24, 'salary': 11.1}

    发现被代理的name属性并没有被设置对应的值,所以__dict__中没有'name',那是因为实例化的时候执行了__init__,所以也执行了__set__,
    但是在__set__中并没有真正的操作进行设置

      2、所以要想真正的对属性进行代理,对属性进行设置、获取和删除值,则需要通过操作底层__dict__字典,如下:

     1 # 定义描述符
     2 class Desc_type:
     3     def __init__(self,key,value_type):             #传入key用来操作底层属性字典,value_type用来表示期望的数据类型
     4         self.key = key
     5         self.value_type = value_type
     6     def __get__(self, instance, owner):
     7         print('执行了__get__')
     8         return instance.__dict__[self.key]              #return p2.name
     9     def __set__(self, instance, value):
    10         print('执行了__set__',self)
    11         if not isinstance(value,str):                   #用来判断用户传入的是否符合要求
    12             raise TypeError('%s 传入的不是 %s'%(self.key,self.value_type))      #抛出类型异常,提示用户程序终止
    13         instance.__dict__[self.key] = value             #符合要求,则设置属性对应的值
    14     def __delete__(self, instance):
    15         print('执行了__delete__')
    16         instance.__dict__.pop(self.key)
    17 
    18 # 定义一个人的类(被代理的类)
    19 class People:
    20     name = Desc_type('name',str)  # 用描述符代理了name这个属性,相当于执行了Desc_type中的self.__set__
    21     age = Desc_type('age',int)
    22     salary = Desc_type('salary',float)
    23     def __init__(self, name, age, salary):
    24         self.name = name
    25         self.age = age
    26         self.salary = salary
    27 p2 = People('Meanwey',24,11.1)
    28 
    29 #访问
    30 print(p2.name)
    31 
    32 #赋值
    33 p2.name = 'Jery'
    34 p2.age = 18.1           #没有传入整型的数据,则 ----TypeError: age 传入的不是 <class 'int'>

    六、总结
      1、描述符就是一个类(新式类);
      2、描述符分为数据描述和非数据描述符,区别在于前者有__set__方法,后者没有;
      3、描述符的使用要遵循优先级:类属性>数据描述符>实例属性>非数据描述符>找不到(__getattr__);
      4、描述符方法中的self表示的是描述符实例化的对象,instance表示的是被描述(代理)的类实例化的对象,owner表示的是被描述(代理)的类,value表示的是设置到被描述(代理)属性的值。

  • 相关阅读:
    洛谷 P4297 [NOI2006]网络收费
    bzoj 5072: [Lydsy1710月赛]小A的树
    树形背包
    loj #2071. 「JSOI2016」最佳团体
    bzoj 2427: [HAOI2010]软件安装
    bzoj 4987: Tree
    loj #2007. 「SCOI2015」国旗计划
    loj #2006. 「SCOI2015」小凸玩矩阵
    loj #2020. 「AHOI / HNOI2017」礼物
    loj #547. 「LibreOJ β Round #7」匹配字符串
  • 原文地址:https://www.cnblogs.com/Meanwey/p/9898222.html
Copyright © 2011-2022 走看看