zoukankan      html  css  js  c++  java
  • python 面向对象专题(九):特殊方法 (二)__get__、__set__、__delete__ 描述符(二)覆盖型与非覆盖型描述符对比

    前言

    根据是否定义__set__ 方法,描述符可分为两大类。

    实现 __set__ 方法的描述符属于覆盖型描述符,因为虽然描述符是类属性,但是实现 __set__ 方法的话,会覆盖对实例属性的赋值操作。

    没有实现 __set__ 方法的描述符是非覆盖型描述符。如果设置了同名的实例属性,描述符会被遮盖,致使描述符无法处理那个实例的那个属性。

    示例 20-8 descriptorkinds.py:几个简单的类,用于研究描述符的覆盖行为

    ### 辅助函数,仅用于显示 ###
    def cls_name(obj_or_cls):
        cls = type(obj_or_cls)
        if cls is type:
            cls = obj_or_cls
        return cls.__name__.split('.')[-1]
    
    def display(obj):
        cls = type(obj)
        if cls is type:
            return '<class {}>'.format(obj.__name__)
        elif cls in [type(None), int]:
            return repr(obj)
        else:
            return '<{} object>'.format(cls_name(obj))
    def print_args(name, *args):
        pseudo_args = ', '.join(display(x) for x in args)
        print('-> {}.__{}__({})'.format(cls_name(args[0]), name, pseudo_args))
    
    ### 对这个示例重要的类 ###
    class Overriding:  ➊
        """也称数据描述符或强制描述符"""
        def __get__(self, instance, owner):
            print_args('get', self, instance, owner)  ➋
        def __set__(self, instance, value):
            print_args('set', self, instance, value)
    
    class OverridingNoGet:  ➌
        """没有``__get__``方法的覆盖型描述符"""
        def __set__(self, instance, value):
            print_args('set', self, instance, value)
    
    class NonOverriding:  ➍
        """也称非数据描述符或遮盖型描述符"""
        def __get__(self, instance, owner):
            print_args('get', self, instance, owner)
    
    class Managed:  ➎
        over = Overriding()
        over_no_get = OverridingNoGet()
        non_over = NonOverriding()
        def spam(self):  ➏
            print('-> Managed.spam({})'.format(display(self)))

    ❶ 有 __get__ 和 __set__ 方法的典型覆盖型描述符。

    ❷ 在这个示例中,各个描述符的每个方法都调用了 print_args 函数。

    ❸ 没有 __get__ 方法的覆盖型描述符。
    ❹ 没有 __set__ 方法,所以这是非覆盖型描述符。
    ❺ 托管类,使用各个描述符类的一个实例。
    ❻ spam 方法放在这里是为了对比,因为方法也是描述符。
    在接下来的几节中,我们要分析对 Managed 类及其实例做属性读写时
    的行为,还会讨论所定义的各个描述符。

    2 覆盖型描述符

    2.1 覆盖型描述符

    实现 __set__ 方法的描述符属于覆盖型描述符,因为虽然描述符是类属性,但是实现 __set__ 方法的话,会覆盖对实例属性的赋值操作。

    示例 20-9 覆盖型描述符的行为,其中 obj.over 是 Overriding类(见示例 20-8)的实例

        >>> obj = Managed()  ➊
        >>> obj.over  ➋
        -> Overriding.__get__(<Overriding object>, <Managed object>,
            <class Managed>)
        >>> Managed.over  ➌
        -> Overriding.__get__(<Overriding object>, None, <class Managed>)
        >>> obj.over = 7-> Overriding.__set__(<Overriding object>, <Managed object>, 7)
        >>> obj.over  ➎
        -> Overriding.__get__(<Overriding object>, <Managed object>,
            <class Managed>)
        >>> obj.__dict__['over'] = 8>>> vars(obj)  ➐
        {'over': 8}
        >>> obj.over  ➑
        -> Overriding.__get__(<Overriding object>, <Managed object>,
            <class Managed>)

    ❶ 创建供测试使用的 Managed 对象。
    ❷ obj.over 触发描述符的 __get__ 方法,第二个参数的值是托管实例 obj。
    ❸ Managed.over 触发描述符的 __get__ 方法,第二个参数(instance)的值是 None。
    ❹ 为 obj.over 赋值,触发描述符的 __set__ 方法,最后一个参数的值是 7。
    ❺ 读取 obj.over,仍会触发描述符的 __get__ 方法。
    ❻ 跳过描述符,直接通过 obj.__dict__ 属性设值。
    ❼ 确认值在 obj.__dict__ 属性中,在 over 键名下。
    ❽ 然而,即使是名为 over 的实例属性,Managed.over 描述符仍会覆盖读取 obj.over 这个操作。

    2.2 没有 __get__ 方法的覆盖型描述符

    示例 20-10 没有 __get__ 方法的覆盖型描述符,其中obj.over_no_get 是 OverridingNoGet 类(见示例 20-8)的实例

        >>> obj.over_no_get  ➊
        <__main__.OverridingNoGet object at 0x665bcc>
        >>> Managed.over_no_get  ➋
        <__main__.OverridingNoGet object at 0x665bcc>
        >>> obj.over_no_get = 7-> OverridingNoGet.__set__(<OverridingNoGet object>, <Managed object>, 7)
        >>> obj.over_no_get  ➍
        <__main__.OverridingNoGet object at 0x665bcc>
        >>> obj.__dict__['over_no_get'] = 9>>> obj.over_no_get  ➏
        9
        >>> obj.over_no_get = 7-> OverridingNoGet.__set__(<OverridingNoGet object>, <Managed object>, 7)
        >>> obj.over_no_get  ➑
        9

    ❶ 这个覆盖型描述符没有 __get__ 方法,因此,obj.over_no_get 从类中获取描述符实例。
    ❷ 直接从托管类中读取描述符实例也是如此。
    ❸ 为 obj.over_no_get 赋值会触发描述符的 __set__ 方法。
    ❹ 因为 __set__ 方法没有修改属性,所以在此读取 obj.over_no_get获取的仍是托管类中的描述符实例。
    ❺ 通过实例的 __dict__ 属性设置名为 over_no_get 的实例属性。
    ❻ 现在,over_no_get 实例属性会遮盖描述符,但是只有读操作是如此。
    ❼ 为 obj.over_no_get 赋值,仍然经过描述符的 __set__ 方法处理。
    ❽ 但是读取时,只要有同名的实例属性,描述符就会被遮盖

    3 非覆盖型描述符

    示例 20-11 非覆盖型描述符的行为,其中 obj.non_over 是NonOverriding 类(见示例 20-8)的实例

        >>> obj = Managed()
        >>> obj.non_over  ➊
        -> NonOverriding.__get__(<NonOverriding object>, <Managed object>,
            <class Managed>)
        >>> obj.non_over = 7>>> obj.non_over  ➌
        7
        >>> Managed.non_over  ➍
        -> NonOverriding.__get__(<NonOverriding object>, None, <class Managed>)
        >>> del obj.non_over  ➎
        >>> obj.non_over  ➏
        -> NonOverriding.__get__(<NonOverriding object>, <Managed object>,
            <class Managed>)

    ❶ obj.non_over 触发描述符的 __get__ 方法,第二个参数的值是obj。
    ❷ Managed.non_over 是非覆盖型描述符,因此没有干涉赋值操作的__set__ 方法。
    ❸ 现在,obj 有个名为 non_over 的实例属性,把 Managed 类的同名描述符属性遮盖掉。
    ❹ Managed.non_over 描述符依然存在,会通过类截获这次访问。
    ❺ 如果把 non_over 实例属性删除了……
    ❻ 那么,读取 obj.non_over 时,会触发类中描述符的 __get__ 方法;但要注意,第二个参数的值是托管实例。

    在上述几个示例中,我们为几个与描述符同名的实例属性赋了值,结果依描述符中是否有 __set__ 方法而有所不同。

    依附在类上的描述符无法控制为类属性赋值的操作。其实,这意味着为类属性赋值能覆盖描述符属性

    4 在类中覆盖描述符

    不管描述符是不是覆盖型,为类属性赋值都能覆盖描述符。这是一种猴子补丁技术,不过在示例 20-12 中,我们把描述符替换成了整数,这其实会导致依赖描述符的类不能正确地执行操作。

    示例 20-12 通过类可以覆盖任何描述符

        >>> obj = Managed()  ➊
        >>> Managed.over = 1>>> Managed.over_no_get = 2
        >>> Managed.non_over = 3
        >>> obj.over, obj.over_no_get, obj.non_over  ➌
        (1, 2, 3)

    ❶ 为后面的测试新建一个实例。
    ❷ 覆盖类中的描述符属性。
    ❸ 描述符真的不见了。


    示例 20-12 揭示了读写属性的另一种不对等:读类属性的操作可以由依附在托管类上定义有 __get__ 方法的描述符处理,但是写类属性的操作不会由依附在托管类上定义有 __set__ 方法的描述符处理。



  • 相关阅读:
    观后感(追番记)...
    网络流24题
    动物园
    [HNOI2016]网络
    部落战争(最小路径点覆盖)
    P4313 文理分科(最小割)
    P2774 方格取数问题(最小割)
    P2472 [SCOI2007]蜥蜴(最大流)
    P1231 教辅的组成(最大流)
    最短路计数(SPFA× Dijkstra√)
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12916380.html
Copyright © 2011-2022 走看看