• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Python基础详解之描述符

    一、描述符定义

    描述符是一种类,我们把实现了__get__()、__set__()和__delete__()中的其中任意一种方法的类称之为描述符。

    描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在被使用类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例,我们可以通过查看实例和类的字典来确认这一点。

    描述符是实现大部分Python类特性中最底层的数据结构的实现手段,我们常使用的@classmethod、@staticmethd、@property、甚至是__slots__等属性都是通过描述符来实现的。它是很多高级库和框架的重要工具之一,是使用到装饰器或者元类的大型框架中的一个非常重要组件。

    如下示例一个描述符及引用描述符类的代码:

    class Descriptors:
     
        def __init__(self, key, value_type):
            self.key = key
            self.value_type = value_type
     
        def __get__(self, instance, owner):
            print("===> 执行Descriptors的 get")
            return instance.__dict__[self.key]
     
        def __set__(self, instance, value):
            print("===> 执行Descriptors的 set")
            if not isinstance(value, self.value_type):
                raise TypeError("参数%s必须为%s" % (self.key, self.value_type))
            instance.__dict__[self.key] = value
     
        def __delete__(self, instance):
            print("===>  执行Descriptors的delete")
            instance.__dict__.pop(self.key)
     
     
    class Person:
        name = Descriptors("name", str)
        age = Descriptors("age", int)
     
        def __init__(self, name, age):
            self.name = name
            self.age = age
     
     
    person = Person("xu", 15)
    print(person.__dict__)
    person.name
    person.name = "xu-1"
    print(person.__dict__)
    # 结果:
    #     ===> 执行Descriptors的 set
    #     ===> 执行Descriptors的 set
    #     {'name': 'xu', 'age': 15}
    #     ===> 执行Descriptors的 get
    #     ===> 执行Descriptors的 set
    #     {'name': 'xu-1', 'age': 15}
    

    其中,Descriptors类就是一个描述符,Person是使用描述符的类。

    类的__dict__属性是类的一个内置属性,类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里。

    在输出描述符的变量时,会调用描述符中的__get__方法,在设置描述符变量时,会调用描述符中的__set__方法。

    二、描述符的种类和优先级

    描述符分为数据描述符和非数据描述符。

    至少实现了内置__set__()和__get__()方法的描述符称为数据描述符;实现了除__set__()以外的方法的描述符称为非数据描述符。

    描述符的优先级的高低顺序:类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()。

    在上述“描述符定义”章节的例子中,实例person的属性优先级低于数据描述符Descriptors,所以在赋值或获取值过程中,均调用了描述符的方法。

    class Descriptors:
        def __get__(self, instance, owner):
            print("===> 执行 Descriptors get")
     
        def __set__(self, instance, value):
            print("===> 执行 Descriptors set")
     
        def __delete__(self, instance):
            print("===> 执行 Descriptors delete")
     
     
    class University:
        name = Descriptors()
     
        def __init__(self, name):
            self.name = name
    

    类属性 > 数据描述符

    # 类属性 > 数据描述符
    # 在调用类属性时,原来字典中的数据描述法被覆盖为 XX-XX
    print(University.__dict__)  # {..., 'name': __main__.Descriptors object at 0x7ff8c0eda278>,}
     
    University.name = "XX-XX"
    print(University.__dict__)  # {..., 'name': 'XX-XX',}
    

    数据描述符 > 实例属性

    # 数据描述符 > 实例属性
    # 调用时会现在实例里面找,找不到name属性,到类里面找,在类的字典里面找到 'name': __main__.Descriptors object at 0x7fce16180a58>
    # 初始化时调用 Descriptors 的 __set__; un.name 调用  __get__
    print(University.__dict__)
    un = University("xx-xx")
    un.name
    # 结果:
    #     {..., 'name': __main__.Descriptors object at 0x7ff8c0eda278>,}
    #     ===> 执行 Descriptors set
    #     ===> 执行 Descriptors get
    

    下面是测试 实例属性、 非数据描述符、__getattr__ 使用代码

    class Descriptors:
        def __get__(self, instance, owner):
            print("===>2 执行 Descriptors get")
     
     
    class University:
        name = Descriptors()
     
        def __init__(self, name):
            self.name = name
     
        def __getattr__(self, item):
            print("---> 查找 item = {}".format(item))
    

    实例属性 > 非数据描述符

    # 实例属性 > 非数据描述符
    # 在创建实例的时候,会在属性字典中添加 name,后面调用 un2.name 访问,都是访问实例字典中的 name
    un2 = University("xu2")
    print(un2.name)  # xu    并没有调用到  Descriptors 的 __get__
    print(un2.__dict__)  # {'name': 'xu2'}
    un2.name = "xu-2"
    print(un2.__dict__)  # {'name': 'xu-2'}
    

    非数据描述符 > 找不到的属性触发__getattr__

    # 非数据描述符 > 找不到的属性触发__getattr__
    # 找不到 name1 使用 __getattr__
    un3 = University("xu3")
    print(un3.name1)  # ---> 查找 item = name1

    三、描述符的应用

    使用描述符检验数据类型

    class Typed:
        def __init__(self, key, type):
            self.key = key
            self.type = type
     
        def __get__(self, instance, owner):
            print("---> get 方法")
            # print("instance = {}, owner = {}".format(instance, owner))
            return instance.__dict__[self.key]
     
        def __set__(self, instance, value):
            print("---> set 方法")
            # print("instance = {}, value = {}".format(instance, value))
            if not isinstance(value, self.type):
                # print("设置name的值不是字符串: type = {}".format(type(value)))
                # return
                raise TypeError("设置{}的值不是{},当前传入数据的类型是{}".format(self.key, self.type, type(value)))
            instance.__dict__[self.key] = value
     
        def __delete__(self, instance):
            print("---> delete 方法")
            # print("instance = {}".format(instance))
            instance.__dict__.pop(self.key)
     
     
    class Person:
        name = Typed("name", str)
        age = Typed("age", int)
     
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
     
     
    p1 = Person("xu", 99, 100.0)
    print(p1.__dict__)
    p1.name = "XU1"
    print(p1.__dict__)
    del p1.name
    print(p1.__dict__)
    # 结果:
    #     ---> set 方法
    #     ---> set 方法
    #     {'name': 'xu', 'age': 99, 'salary': 100.0}
    #     ---> set 方法
    #     {'name': 'XU1', 'age': 99, 'salary': 100.0}
    #     ---> delete 方法
    #     {'age': 99, 'salary': 100.0}
    

    四、描述符 + 类装饰器  (给 Person类添加类属性)

    类装饰器,道理和函数装饰器一样

    def Typed(**kwargs):
        def deco(obj):
            for k, v in kwargs.items():
                setattr(obj, k, v)
            return obj
        return deco
     
     
    @Typed(x=1, y=2)  # 1、Typed(x=1, y=2) ==> deco   2、@deco ==> Foo = deco(Foo)
    class Foo:
        pass
     
     
    # 通过类装饰器给类添加属性
    print(Foo.__dict__)  # {......, 'x': 1, 'y': 2}
    print(Foo.x)
    

    使用描述符和类装饰器给 Person类添加类属性

    """
    描述符 + 类装饰器
    """
    class Typed:
        def __init__(self, key, type):
            self.key = key
            self.type = type
     
        def __get__(self, instance, owner):
            print("---> get 方法")
            # print("instance = {}, owner = {}".format(instance, owner))
            return instance.__dict__[self.key]
     
        def __set__(self, instance, value):
            print("---> set 方法")
            # print("instance = {}, value = {}".format(instance, value))
            if not isinstance(value, self.type):
                # print("设置name的值不是字符串: type = {}".format(type(value)))
                # return
                raise TypeError("设置{}的值不是{},当前传入数据的类型是{}".format(self.key, self.type, type(value)))
            instance.__dict__[self.key] = value
     
        def __delete__(self, instance):
            print("---> delete 方法")
            # print("instance = {}".format(instance))
            instance.__dict__.pop(self.key)
     
     
    def deco(**kwargs):
        def wrapper(obj):
            for k, v in kwargs.items():
                setattr(obj, k, Typed(k, v))
            return obj
        return wrapper
     
     
    @deco(name=str, age=int)
    class Person:
        # name = Typed("name", str)
        # age = Typed("age", int)
     
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
     
     
    p1 = Person("xu", 99, 100.0)
    print(Person.__dict__)
    print(p1.__dict__)
    p1.name = "XU1"
    print(p1.__dict__)
    del p1.name
    print(p1.__dict__)
    # 结果:
    #     ---> set 方法
    #     ---> set 方法
    #     {..., 'name': __main__.Typed object at 0x7f3d79729dd8>, 'age': __main__.Typed object at 0x7f3d79729e48>}
    #     {'name': 'xu', 'age': 99, 'salary': 100.0}
    #     ---> set 方法
    #     {'name': 'XU1', 'age': 99, 'salary': 100.0}
    #     ---> delete 方法
    #     {'age': 99, 'salary': 100.0}
    

    五、利用描述符自定义 @property

    class Lazyproperty:
        def __init__(self, func):
            self.func = func
     
        def __get__(self, instance, owner):
            print("===> Lazypropertt.__get__ 参数: instance = {}, owner = {}".format(instance, owner))
            if instance is None:
                return self
            res = self.func(instance)
            setattr(instance, self.func.__name__, res)
            return self.func(instance)
     
        # def __set__(self, instance, value):
        #     pass
     
     
    class Room:
     
        def __init__(self, name, width, height):
            self.name = name
            self.width = width
            self.height = height
     
        # @property  # area=property(area)
        @Lazyproperty  # # area=Lazyproperty(area)
        def area(self):
            return self.width * self.height
     
    #  @property 测试代码
    # r = Room("房间", 2, 3)
    # print(Room.__dict__)  # {..., 'area': property object at 0x7f8b42de5ea8>,}
    # print(r.area)  # 6
     
    # r2 = Room("房间2", 2, 3)
    # print(r2.__dict__)  # {'name': '房间2', 'width': 2, 'height': 3}
    # print(r2.area)
     
    # print(Room.area)  # __main__.Lazyproperty object at 0x7faabd589a58>
     
    r3 = Room("房间3", 2, 3)
    print(r3.area)
    print(r3.area)
    # 结果(只计算一次):
    #     ===> Lazypropertt.__get__ 参数: instance = __main__.Room object at 0x7fd98e3757b8>, owner = class '__main__.Room'>
    #     6
    #     6
    

    六、property 补充

    class Foo:
     
        @property
        def A(self):
            print("===> get A")
     
        @A.setter
        def A(self, val):
            print("===> set A, val = {}".format(val))
     
        @A.deleter
        def A(self):
            print("===> del A")
     
     
    f = Foo()
    f.A         # ===> get A
    f.A = "a"   # ===> set A, val = a
    del f.A     # ===> del A
     
     
     
    class Foo:
     
        def get_A(self):
            print("===> get_A")
     
        def set_A(self, val):
            print("===> set_A, val = {}".format(val))
     
        def del_A(self):
            print("===> del_A")
     
        A = property(get_A, set_A, del_A)
     
     
    f = Foo()
    f.A         # ===> get_A
    f.A = "a"   # ===> set_A, val = a
    del f.A     # ===> del_A
    

    到此这篇关于Python基础详解之描述符的文章就介绍到这了,更多相关Python描述符内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • Python描述符descriptor使用原理解析
    • python实现装饰器、描述符
    • 详解Python中的Descriptor描述符类
    • Python黑魔法Descriptor描述符的实例解析
    • Python 的描述符 descriptor详解
    • 解密Python中的描述符(descriptor)
    • 详解Python描述符的工作原理
    上一篇:详解Python 3.10 中的新功能和变化
    下一篇:4种非常实用的python内置数据结构
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Python基础详解之描述符 Python,基础,详解,之,描述,