• 描述符__get__,__set__,__delete__和析构方法__del__


    描述符__get__,__set__,__delete__

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

    描述就是一个新式类,这个类至少要实现上述三个方法的一个
    class 描述符:
        def __get__():
            pass
        def __set__():
            pass
        def __delete__():
            pass
    
    class 类:
        name=描述符()
    
    obj=类()
    obj.name #get
    obj.name='egon' #set
    del obj.name #delete
    
    
    2、描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
    class Foo:
        def __get__(self, instance, owner):
            print('触发get')
        def __set__(self, instance, value):
            print('触发set')
        def __delete__(self, instance):
            print('触发delete')
    
    class Bar:
        x = Foo()   #描述符必须写在类的属性中
        def __init__(self,n):
            self.x = n
    
    #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
    f1=Foo()  #并不会调用
    f1.name='egon'  #赋值
    f1.name  #调用
    del f1.name  #删除
    b1 = Bar('b')  #触发__set__
    b1.x  #触发__get__
    总结:但凡是操作被代理的self.x都会找代理的类
    3、描述符分两种
    一 数据描述符:至少实现了__get__()和__set__()
    1 class Foo:
    2     def __set__(self, instance, value):
    3         print('set')
    4     def __get__(self, instance, owner):
    5         print('get')
    
    

      二 非数据描述符:没有实现__set__()

    1 class Foo:
    2     def __get__(self, instance, owner):
    3         print('get')

    4  描述符的参数

    #Typep数据描述符
    class Typep:
        def __init__(self,name):
            self.name = name
        def __get__(self, instance, owner):
            print('__get__执行')
            self.instance = instance    #instance 传过来的就是People类的一个实例,也就是p1
            self.owner = owner          #owner 传过来的就是instance的拥有者,也就是实例化对象的类
    
    
        def __set__(self, instance, value):
            print('__set__执行')
            self.instance = instance    #instance 传过来的就是People类的一个实例,也就是p1
                                        #<__main__.People object at 0x000000EA07077208>
            self.value = value          #就是p1传入过来的name属性的值
                                        #zhaok
            print(self)
            instance.__dict__[self.name]=value
    
        def __delete__(self, instance):
            print('__delete__执行')
            self.instance = instance
    
    class People:
        name = Typep('name')  #name被Typep数据描述符代理,被代理过后的所有对name的操作都会去找代理类
        def __init__(self,name,age,sal):    #触发过后,正常传值,但是name被代理     数据描述符优先级高于实例属性
            self.name = name
            self.age = age
            self.sal = sal
    
    p1 = People('zhaok',18,33.3)     #实例化一个对象p1   实例化会触发People类的构造方法也就是__init__方法

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

    
    
    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    
    #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
    
    #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
    People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__()
    
    People.name='egon' #那赋值呢,我去,并没有触发__set__()
    del People.name #赶紧试试del,我去,也没有触发__delete__()
    #结论:描述符对类没有作用-------->傻逼到家的结论
    
    '''
    原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
    People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
    
    People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
    del People.name #同上
    '''
    
    类属性>数据描述符
    类属性>数据描述符
    
    
    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    
    p1=People('egon',18)
    
    #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
    p1.name='egonnnnnn'
    p1.name
    print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
    del p1.name
    数据描述符>实例属性
    class Foo:
        def func(self):
            print('我胡汉三又回来了')
    f1=Foo()
    f1.func() #调用类的方法,也可以说是调用非数据描述符
    #函数是一个非数据描述符对象(一切皆对象么)
    print(dir(Foo.func))
    print(hasattr(Foo.func,'__set__'))
    print(hasattr(Foo.func,'__get__'))
    print(hasattr(Foo.func,'__delete__'))
    #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了
    #笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
    #函数就是一个由非描述符类实例化得到的对象
    #没错,字符串也一样
    
    
    f1.func='这是实例属性啊'
    print(f1.func)
    
    del f1.func #删掉了非数据
    f1.func()
    实例属性>非数据描述符
    class Foo:
        def __set__(self, instance, value):
            print('set')
        def __get__(self, instance, owner):
            print('get')
    class Room:
        name=Foo()
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
    
    #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
    #对实例的属性操作,触发的都是描述符的
    r1=Room('厕所',1,1)
    r1.name
    r1.name='厨房'
    
    
    
    class Foo:
        def __get__(self, instance, owner):
            print('get')
    class Room:
        name=Foo()
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
    
    #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
    #对实例的属性操作,触发的都是实例自己的
    r1=Room('厕所',1,1)
    r1.name
    r1.name='厨房'
    再次验证:实例属性>非数据描述符
    class Foo:
        def func(self):
            print('我胡汉三又回来了')
    
        def __getattr__(self, item):
            print('找不到了当然是来找我啦',item)
    f1=Foo()
    
    f1.xxxxxxxxxxx
    非数据描述符>找不到

    6 描述符使用

    众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

    #Typep数据描述符
    class Typep:
        def __init__(self,key,expected_type):
            self.key = key
            self.expected_type = expected_type
        def __get__(self, instance, owner):
            print('__get__执行')
            # self.instance = instance    #instance 传过来的就是People类的一个实例,也就是p1
            # self.owner = owner          #owner 传过来的就是instance的拥有者,也就是实例化对象的类
            return instance.__dict__[self.key]  #返回p1key值对应的value值
    
        def __set__(self, instance, value):
            print('__set__执行')
            # self.instance = instance    #instance 传过来的就是People类的一个实例,也就是p1
                                        #<__main__.People object at 0x000000EA07077208>
            # self.value = value          #就是p1传入过来的name属性的值
                                        #zhaok
            if not isinstance(value,self.expected_type):    #判断如果传入的信息,不是想要的类型
                raise TabError('你传入的%s不是%s类型'%(value,self.expected_type))   #直接抛出异常,终止服务
            instance.__dict__[self.key]=value  #p1.__dict__[key] = value    #如果是,直接写入字典中
    
        def __delete__(self, instance):
            print('__delete__执行')
            # self.instance = instance
            instance.__dict__.pop[self.key] #删除
    class People:
        name = Typep('name',str)  #name被Typep数据描述符代理,被代理过后的所有对name的操作都会去找代理类
        age = Typep('age',int)
        def __init__(self,name,age,sal):    #触发过后,正常传值,但是name被代理     数据描述符优先级高于实例属性
            self.name = name
            self.age = age
            self.sal = sal
    
    p1 = People('zhaok',18,33.3)     #实例化一个对象p1   实例化会触发People类的构造方法也就是__init__方法
    print(p1.name)

    __del__

    析构方法,当对象在内存中被释放时,自动触发执行。

    注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    简单示范:

    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    del f1
    print('------->')
    
    #输出结果
    执行我啦
    ------->
  • 相关阅读:
    《Linux命令行与shell脚本编程大全 第3版》高级Shell脚本编程---07
    《Linux命令行与shell脚本编程大全 第3版》高级Shell脚本编程---05
    shell-查看手机分辨率
    imageView-scaleType 图片压缩属性
    actionbar-displayOptions 属性分析
    setting.system-全局属性的设定
    ActivityChooserView-如何隐藏选择的应用图标
    mIsFunui-判断Funui方法
    setting-在设置中添加新的选项
    install-软件安装跟push的区别
  • 原文地址:https://www.cnblogs.com/shangpolu/p/6233835.html
Copyright © 2020-2023  润新知