• 描述符


    目录

    描述符

    什么是描述符

    描述符的作用

    描述符初探

    描述符实战

    雏形

    进阶1

    进阶2 添加类型限制

    逐一限制

    终极boss 类的装饰器

    描述符

    什么是描述符

    描述符本质是一个类,在设计上至少实现了__set__() __get__() __delete__() 其中之一,这也被称为描述符协议。

    __set__():为一个属性赋值时触发

    __get__():获取一个属性时触发

    __delete__():用del删除一个属性时触发

    描述符的作用

    把描述符定义成另一个类的属性,前提是不能定义到构造函数中,此时描述符就可以代理另一个类的属性。所以注意了,由描述符本身实例化的对象进行获取、赋值、删除操作并不会触发这三个方法。

    描述符初探

    #描述符Str
    
    class Str:
    
        def __get__(self, instance, owner):
    
          print('Str调用')
    
        def __set__(self, instance, value):
    
              print('Str设置...')
    
        def __delete__(self, instance):
    
             print('Str删除...')
    
     
    
    #描述符Int
    
    class Int:
    
        def __get__(self, instance, owner):
    
            print('Int调用')
    
        def __set__(self, instance, value):
    
            print('Int设置...')
    
        def __delete__(self, instance):
    
            print('Int删除...')
    
     
    
    class People:
    
        name=Str()
    
        age=Int()
    
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
    
            self.name=name
    
            self.age=age
    
     
    
     
    
    p1=People('alex',18)
    
     
    
    #输出结果
    
    Str设置...
    
    Int设置...
    
     
    
    #描述符Str的使用
    
    p1.name
    
    p1.name='egon'
    
    del p1.name
    
     
    
    #输出结果
    
    Str调用
    
    Str设置...
    
    Str删除..
    
    #描述符Int的使用
    
    p1.age
    
    p1.age=18
    
    del p1.age
    
     
    
    #输出结果
    
    Int调用
    
    Int设置...
    
    Int删除...
    
     
    
     
    
    print(p1.__dict__)
    
    print(People.__dict__)
    
     
    
    # 输出结果可能让你费解
    
    {}
    
    {'__module__': '__main__', 'name': <__main__.Str object at 0x00000033945DEA58>, 'age': <__main__.Int object at 0x00000033945DE2B0>, '__init__': <function People.__init__ at 0x0000003394753950>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
    
    #补充,看一下类型是否一致
    
    print(type(p1) == People)
    
    print(type(p1).__dict__ == People.__dict__)
    
     
    
    True
    
    True    
    View Code

    从初始化到一系列的调用、赋值、删除,我们都不难理解触发,但是在输出__dict__的时候,我们发现实例化的对象内容为空,类对象的内容输出就很明确的看到了其中的两个属性是类对象。所以我们可以自己理解为被代理的类自身只知道有什么属性,但是是什么样子的不清楚了,在代理那里保存着呢!

    描述符也有自身要遵守的优先级问题:

    1. 类属性
    2. 数据描述符(至少实现了__get__()和__set__()
    3. 实例属性
    4. 非数据描述符(没有实现__set__()
    5. 找不到的属性触发__getattr__()

    在此,不对优先级再做验证,出现问题是可以考虑是不是这方面原因。

    描述符实战

    实现用描述符实现类型控制

    雏形

    class Str:
        def __init__(self, name):
            self.name = name
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name = Str('name')
    
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    p1 = People('egon', 18, 3231.3)
    
    # 调用
    print(p1.__dict__)
    p1.name
    print(p1.name)
    
    # 赋值
    print(p1.__dict__)
    p1.name = 'egonlin'
    print(p1.__dict__)
    
    # 删除
    print(p1.__dict__)
    del p1.name
    print(p1.__dict__)
    
    set---> <__main__.People object at 0x000000D9C28CFC18> egon
    {'name': 'egon', 'age': 18, 'salary': 3231.3}
    get---> <__main__.People object at 0x000000D9C28CFC18> <class '__main__.People'>
    get---> <__main__.People object at 0x000000D9C28CFC18> <class '__main__.People'>
    egon
    {'name': 'egon', 'age': 18, 'salary': 3231.3}
    set---> <__main__.People object at 0x000000D9C28CFC18> egonlin
    {'name': 'egonlin', 'age': 18, 'salary': 3231.3}
    {'name': 'egonlin', 'age': 18, 'salary': 3231.3}
    delete---> <__main__.People object at 0x000000D9C28CFC18>
    {'age': 18, 'salary': 3231.3}
    View Code

    进阶1

    如果我在刚才代码里用类名调用会报错,说instance是空,所以需要修改get方法:

    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]
    View Code

    再次调用的时候就不会再报错了 输出:get---> None <class '__main__.People'>

    进阶2 添加类型限制

    class Str:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type): #如果不是期望的类型,则抛出异常
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name',str) #新增类型限制str
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常
    
    
    set---> <__main__.People object at 0x00000053639A4438> 123
    Traceback (most recent call last):
      File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 28, in <module>
        p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常
      File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 24, in __init__
        self.name=name
      File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 14, in __set__
        raise TypeError('Expected %s' %str(self.expected_type))
    TypeError: Expected <class 'str'>
    View Code

    逐一限制

    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Typed('name',str)
        age=Typed('name',int)
        salary=Typed('name',float)
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    View Code

    但是如果属性很多,是不是这样子就有点麻烦

    终极boss 类的装饰器

      
    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            for name,expected_type in kwargs.items():
                setattr(cls,name,Typed(name,expected_type))
            return cls
        return decorate
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    print(People.__dict__)
    p1=People('egon',18,3333.3)
    
    
    类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
    {'__module__': '__main__', '__init__': <function People.__init__ at 0x000000BCA4A43950>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, 'name': <__main__.Typed object at 0x000000BCA48CEBA8>, 'age': <__main__.Typed object at 0x000000BCA4A3FC18>, 'salary': <__main__.Typed object at 0x000000BCA4A3FC88>}
    set---> <__main__.People object at 0x000000BCA4A3FCF8> egon
    set---> <__main__.People object at 0x000000BCA4A3FCF8> 18
    set---> <__main__.People object at 0x000000BCA4A3FCF8> 3333.3
    View Code
  • 相关阅读:
    Linux显示文件内容常用命令
    Linux文件权限和更改权限
    数据存储及恢复的基本原理
    使用jemter发送HTTPS请求
    运行Jmeter时,出现java.util.prefs.WindowsPreferences <init>异常警告
    Server08AD域安装以及推送
    SVN服务器和客户端搭建
    selenium常见操作
    TestNG 入门教程
    ant+TestNG-xslt生成selenium测试报告
  • 原文地址:https://www.cnblogs.com/yuliangkaiyue/p/9570566.html
Copyright © 2020-2023  润新知