zoukankan      html  css  js  c++  java
  • Python中属性的查找过程分析

    一. 预备知识

      1) 类中实现了__get__()__set__()__delete__()中任意一个方法,就是一个属性描述符

      2) 数据属性描述符: 至少实现__set__()__get__()方法

      3) 非数据属性描述符: 实现除__set__()以外的方法

      4) 使用属性描述符后, 可以在为属性赋值时增加一些逻辑判断

      5) 假设obj是实例化对象, 那么 obj.name 等价于 getattr(obj, "name")

    二. 属性的查找过程分析

    2.1 __getattribute__方法是访问属性的入口

    直接说结论:

      属性查找优先级: __getattribute__方法 > 实例属性

      __getattribute__方法是所有属性调用的入口, 需要注意的是这个方法如果使用不当,会导致程序死循环

    # -*- coding: utf-8 -*-
    
    
    class Person(object):
        def __init__(self):
            self.name = "chinablue"
            self.age = 18
    
        def __getattribute__(self, item):
            print(f"调用了__getattribute__方法")
            if "age" == item:
                return 38
            return object.__getattribute__(self, item)
    
    
    p = Person()
    
    print(p.__dict__)  # 实例属性信息: {'name': 'chinablue'}
    
    print(p.name)  # 实例中有name属性: chinablue
    print(p.age)  # 实例中有age属性,但__getattribute__方法拦截并修改了age属性: 38
    # print(p.job)  # 如果实例对象访问的属性不存在,也会访问__getattribute__方法

    2.2 类属性对属性查找的影响

    直接说结论:

      属性查找优先级实例属性 > 类属性

      实例没有的属性,会去类属性中查找

    # -*- coding: utf-8 -*-
    
    
    class Person(object):
        # age是类属性
        age = 18
    
        def __init__(self):
            # name是实例属性
            self.name = "chinablue"
    
    
    p = Person()
    
    print(p.__dict__)  # 实例属性信息: {'name': 'chinablue'}
    print({k: v for k, v in Person.__dict__.items() if not k.startswith("__")})  # 类的属性信息: {'age': 18}
    
    print(p.name)  # 实例中有name属性: chinablue
    print(p.age)  # 实例中没有age属性,类中有age属性: 18

    2.3 __getattr__方法对属性查找的影响

    直接说结论:

      属性查找优先级实例属性 > 类属性 > __getattr__方法

      如果实例访问的属性不存在,则会抛出AttributeError异常

      如果实例中定义了__getattr__方法, 当实例访问的属性不存在时, 会去访问__getattr__方法

    # -*- coding: utf-8 -*-
    
    
    class Person(object):
        def __init__(self):
            # name是实例属性
            self.name = "chinablue"
    
        # 当实例对象找不到属性时,会来调用__getattr__方法
        def __getattr__(self, item):
            return "djtest"
    
    
    p = Person()
    
    print(p.__dict__)  # 实例属性信息: {'name': 'chinablue'}
    
    print(p.name)  # 实例中有name属性: chinablue
    print(p.job)  # 实例中没有job属性,会调用__getattr__函数: djtest

    另外, 如果实例对象中同时存在__getattr__方法和__getattribute__方法, 代码如下:

    # -*- coding: utf-8 -*-
    
    
    class Person(object):
        def __init__(self):
            ...
    
        def __getattribute__(self, item):
            print(f"调用了__getattribute__方法")
            return object.__getattribute__(self, item)
    
        def __getattr__(self, item):
            print(f"调用了__getattr__方法")
            raise AttributeError(f"{item}属性不存在")
    
    
    p = Person()
    
    print(p.name)  # 实例中没有name属性,先调用__getattribute__方法, 再调用__getattr__方法
    View Code

    2.4 非数据属性描述符对属性查找的影响

    直接说结论:

      属性查找优先级实例属性 > 非数据属性描述符 > __getattr__方法

      非数据属性描述符的优先级与类属性相当

    # -*- coding: utf-8 -*-
    
    
    class PersonName:
        def __get__(self, instance, owner):
            return "djtest"
    
    
    class PersonAge:
        def __get__(self, instance, owner):
            return 28
    
    
    class Person(object):
        # age和name是非数据描述符
        age = PersonAge()
        name = PersonName()
    
        def __init__(self):
            self.name = "chinablue"
    
    
    p = Person()
    
    print(p.__dict__)  # 实例属性信息: {'name': 'chinablue'}
    print({k: v for k, v in Person.__dict__.items() if not k.startswith("__")})  # 类的属性信息: {'age': <PersonAge object>, 'name': <PersonName object>}
    
    print(p.name)  # 实例中有name属性: chinablue
    print(p.age)  # 实例中没有age属性,类中(非数据描述符中)有age属性: 28

    2.5 数据属性描述符对属性查找的影响

     直接说结论:

      属性查找优先级: 数据属性描述符 > 实例属性

    # -*- coding: utf-8 -*-
    
    
    class PersonName:
        def __get__(self, instance, owner):
            return "djtest"
    
        def __set__(self, instance, value):
            self.name = value
    
    
    class Person(object):
        # name是数据描述符
        name = PersonName()
    
        def __init__(self):
            self.name = "chinablue"
    
    
    p = Person()
    
    print(p.__dict__)  # 实例属性信息: {}
    print({k: v for k, v in Person.__dict__.items() if not k.startswith("__")})  # 类的属性信息: {'name': <__main__.PersonName object at 0x033709B0>}
    
    print(p.name)  # 实例中有name属性: djtest

    三. 本章小结

    对象属性的查找优先级参考如下:

        __getattribute__方法 > 数据属性描述符 > 实例属性 > 类属性 | 非数据属性描述符 > __getattr__方法
  • 相关阅读:
    C#dll中无法找到c++dll中函数的入口
    C#委托及事件处理机制浅析
    lib和dll的例子
    C#中自定义消息,与MFc对比
    MFC消息响应机制 q
    MFC中消息响应机制
    C# 消息处理机制及自定义过滤方式
    c++中__declspec用法总结
    C++中使用接口
    C# 位域[flags] 转
  • 原文地址:https://www.cnblogs.com/reconova-56/p/15179804.html
Copyright © 2011-2022 走看看