zoukankan      html  css  js  c++  java
  • 神奇的描述符(三):覆盖描述符与非覆盖描述符

    描述符可以细分为覆盖型描述符与非覆盖型描述符。

    可以按如下规则区分它们:

    • 实现 __set__ 方法的类,称之为“覆盖型描述符”
    • 没有实现 __set__ 方法的类,但实现了__get__方法的类,称之为“非覆盖型描述符”
    • 同时实现 __set__ 和 __get__ 方法的类,通常称之为“强制描述符”。

     先分别定义三种类型的描述符,及它们托管的类,为后续的测试及验证做准备。

    class OverAll:
    
        """
        描述符是指含有__get__ 、 __set__ 、__delete__方法的类, 
        对于实现了 __set__方法的类,称之为覆盖描述符,
        如果同时也实现了 __get__ 方法,则称之为强制描述符
        """
    
        def __get__(self, instance, owner):
            print("强制描述符 __get__ 被运行")
            return self, instance, owner
    
        def __set__(self, instance, value):
            print("强制描述符 __set__ 被运行")
            return self, instance, value
    
        def __delete__(self, instance):
            print("强制描述符 __delete__ 被运行")
    
    
    class OnlySet:
        """
        对于覆盖型描述符,因为实现了__set__方法,
        会覆盖掉实例属性的赋值操作
        但不会影响类属性的赋值操作
        """
        def __set__(self, instance, value):
            print("覆盖型描述符 __set__ 被运行")
            return self, instance, value
    
    
    class OnlyGet:
        """
        如果描述符没有实现 __set__ 方法(即只有 __get__方法),
        则称之为非覆盖型描述符
        """
    
        def __get__(self, instance, owner):
            print("非覆盖型描述符 __get__ 被运行")
            return self, instance, owner
    
    
    class Spam:
        """
        定义一个类,类属性是上面三种描述符
        """
        over_all = OverAll()
        only_set = OnlySet()
        only_get = OnlyGet()
    
    # 实例化Spam
    spam = Spam()

    覆盖型描述符

    通常情况下,覆盖型描述符会同时实现__get__与__set__方法,但也可以只实现__set__方法。如果在描述符中定义了__set__方法,那么描述符会接管对这个实例属性的赋值。

    在下面这个例子中,对only_set进行赋值,会触发描述符的__set__方法,打印出“覆盖型描述符 __set__ 被运行”

    spam.only_set = 2

    如果直接操作实例属性的__dict__对其进行赋值,描述符不会生效,即使定义了__set__方法。

    spam.__dict__['only_set'] = '描述符不会执行'

    如果覆盖型描述符没有定义__get__方法,那么描述符不会接管实例属性的读取,所以从实例读取only_set时,实际上读取的是类属性only_set,所以会返回描述符对象。

    print(spam.only_set)
    # <__main__.OnlySet object at 0x000001A3C49575C0>

    需要额外注意的是,描述符的__set__方法,只能接管实例属性的赋值,无法接管类属性的赋值,所以对类属性的赋值是无能为力的。

    下面的例子可以看到,即使完整定义了__get__、__set__、__delete__方法的强制描述符, 都无法接管类属性的赋值。

    # 对类属性描述符进行赋值,会直接把描述符覆盖掉,并且不会触发描述符的__set__方法
    Spam.over_all = 1
    # 因为作为类属性的描述符已经被覆盖,所以会打印出 1
    print(Spam.over_all)

    如果需要使用描述符接管类属性的赋值操作,需要通过在元类中定义描述符来实现。


    非覆盖型描述符

    当通过实例访问非覆盖型描述符时,描述符的__get__方法会被执行,打印出“非覆盖型描述符 __get__ 被运行”的字符串。

    # 获取实例的非覆盖型描述符时,会发现描述符的 __get__ 方法被执行
    print(spam.only_get)
    # 非覆盖型描述符 __get__ 被运行
    # (<__main__.OnlyGet object at 0x000002C120637668>, <__main__.Spam object at 0x000002C1206376A0>, <class '__main__.Spam'>)

    与__set__方法不同的是,__get__方法不仅会接管实例属性的访问,还会接管类属性的访问。

    下面的例子中,通过类直接访问only_get,描述符的__get__方法被执行,打印出“非覆盖型描述符 __get__ 被运行”的字符串。

    print(Spam.only_get)
    # 非覆盖型描述符 __get__ 被运行
    # (<__main__.OnlyGet object at 0x00000283A7C27630>, None, <class '__main__.Spam'>)

    如果对实例属性赋值,因为非覆盖型描述符进行*没有*定义__set__方法,无法接管对实例属性的赋值,会在实例中创建一个同名的属性,导致描述符失效。

    # 对实例的非覆盖描述符进行赋值,因为没有 __set__ 方法
    # 所以描述符本身没有接管实例属性的赋值操作
    spam.only_get = 3
    # 这个时候,会实例属性进行取值,就会发现描述符的 __get__ 方法也不会执行
    # 所以打印结果是 3
    print(spam.only_get)

    出现这种情况的原因是通过实例去获取属性时,优先返回的是实例自身的属性,只有在实例自身没有这个属性的情况下,才会到类中查找对应的属性。而描述符本身也是属于类属性,所以受到这个规则的影响。

    spam.only_get = 3 ,对 only_get 进行赋值时,实际上已经在实例中创建出一个同名的属性 only_get, 后续再对 only_get 进行读取,获取的是实例的属性only_get,而不是作为类属性的描述符 only_get。所以 描述符的 __get__ 方法不会触发。

    《Python Cookbook》8.10 让属性具有惰性求值的能力,即是用非覆盖型描述符,让实例属性的读取实现惰性求值,以提高执行性能。核心的逻辑就是在描述符执行__get__方法时,同时对同名实例属性进行赋值,覆盖掉描述符自身。那么在下次读取时,直接读取的便是实例自身的属性,不会再调用描述符的__get__进行运算,以达到提高性能的目的。

    如果需要恢复描述符的正常功能,需要将对应的实例属性删除。下面的代码中,删除了实例属性only_get之后,描述符恢复正常工作。

    # 将实例属性删除,再获取一次 spam.only_get ,就会发现描述符的__get__正常执行了
    del spam.only_get
    print(spam.only_get)
    # (<__main__.OnlyGet object at 0x0000017EFBAC7668>, <__main__.Spam object at 0x0000017EFBAC76A0>, <class '__main__.Spam'>)
  • 相关阅读:
    图书管理系统---基于form组件和modelform改造添加和编辑
    Keepalived和Heartbeat
    SCAN IP 解释
    Configure Active DataGuard and DG BROKER
    Oracle 11gR2
    我在管理工作中積累的九種最重要的領導力 (李開復)
    公募基金公司超融合基础架构与同城灾备建设实践
    Oracle 11g RAC for LINUX rhel 6.X silent install(静默安装)
    11gR2 静默安装RAC 集群和数据库软件
    Setting Up Oracle GoldenGate 12
  • 原文地址:https://www.cnblogs.com/blackmatrix/p/7238004.html
Copyright © 2011-2022 走看看