• __getattribute__ 与 __getattr__


    getattribute

    属性拦截器,所有对象尝试去访问属性的时候,就会调用该方法

    class A:
        x = '类属性x'
        def __init__(self,y):
            self.y = y
    
        def __getattribute__(self,attr_name):
            print("进入到__getattribute__方法")
            return super().__getattribute__(attr_name)
    
    
    a = A('实例属性y')
    
    print(a.y)
    # 输出: 进入到__getattribute__方法
    # 实例属性y
    
    print(a.x)
    #输出: 进入到__getattribute__方法
    #类属性x
    
    
    print(A.x)
    #输出: 类属性x
    

    当对象去访问实例属性、类属性时,都会进入到该实例所在的类【这就是为什么A.x没有输出进入到__getattribute__方法的原因 type(A)是type】的
    __getattribute__(self,attr_name)方法中

    getattr

    当尝试获取一个不存在的属性时发生的行为

    举例:

    class Test:
        name = "测试"
    
        def __getattr__(self,attr_name):
            if attr_name == "height":
                return "不晓得好高"
            else:
                raise AttributeError
    
    test = Test()
    print(test.name) #输出: 测试
    print(test.height)#输出: 不晓得好高
    print(test.gendler)# 报错
    print(Test.height)# 报错, 类Test 所属的类type中没有设置对应height的__getattr__方法
    

    当对象去访问一个该对象实例属性、所属类属性、所属类父类属性都不存在的属性时候,就会进入到该对象所属类的__getattr__(self,attr_name)方法中【这也就是为什么Test.height会报错的原因,因为类Test所属类是type,没有设置这个__getattr__】

    访问顺序

    对象去访问属性的时候,可能存在实例属性、类属性、父类属性、__getattr__方法设置的属性获取 同名的情况,优先级如下:

    class C:
        x = 'C_X'
    
    class D(C):
        x = 'D_X'
    
        def __getattr__(self,attr_name):
            if attr_name == "x":
                return 999
            else:
                raise  AttributeError
    
    
    d = D()
    
    # 优先级1:实例属性
    d.__dict__['x'] = 6
    print(d.x)    #  输出的是6
    
    # 优先级2:类属性
    del d.x
    print(d.x)    #  输出的是类属性 D_X
    
    # 优先级3:父类属性
    del D.x
    print(d.x)    # 输出的是父类属性 C_X
    
    # 优先级4:__getattr__
    del C.x
    print(d.x)    # 输出的是999
    

    优先级如下:

    • 实例属性
    • 类属性
    • 父类属性(mro顺序)
    • __getattr__

    每一次去获取属性,都会先进到__getattribute__ 方法,然后根据上述顺序,如果类属性是描述符:数据描述符优先级>同名实例属性, 实例属性>同名非数据描述符

    拓展:

    __getattribute__只有在访问了才会去调用

    给对象的属性赋值的时候,并不会调用,如果需要获取到属性的值,就会调用

    class A:
        def __init__(self,x):
            self.x =x 
        def __getattribute__(self,attr_name):
            print("进入到__getattribute__方法")
            return super().__getattribute__(attr_name)
    
    
    a = A({"name":"kobe"})
    
    a.x = {"height":"198"}  # 直接赋值,不会调用__getattribute__
    
    a.x['height'] = '200' # 这里相当于要先获取到a.x,然后再去修改a.x的'height'的值,所以触发了访问属性,会调用__getattribute__
    

    访问属性时,并不是直接去调用__getattribute__方法

    其实在点操作符去访问属性的时候,是通过了一个hook函数来执行查找

    def getattr_hook(obj, name):
        "Emulate slot_tp_getattr_hook() in Objects/typeobject.c"
        try:
    		#尝试 执行__getattribute__方法
            return obj.__getattribute__(name)
        except AttributeError:
    		# 如果 该对象所属的类中没有 __getattr__方法,直接报错没有找到该属性
            if not hasattr(type(obj), '__getattr__'):
                raise
    	# 如果该对象所属类有__getattr__方法,就去调用该方法
        return type(obj).__getattr__(obj, name)             # __getattr__
    

    根据上述原理,如果用户直接调用 obj.__getattribute__(),__getattr__() 的补充查找机制就会被绕过。
    测试如下:

    class Test:
        def __getattr__(self,attr_name):
            return "这是__getattr__方法"
    
    test = Test()
    print(test.x)  # 访问一个不存在的属性 会正常走到__getattr__方法去
    
    print(test.__getattribute__('x'))  # 报错没有该属性
    

    上述源码也解释了,实例在访问不存在的属性的时候,调用getattr方法,就会进入到该对象所属类的__getattr__(self,attr_name)方法中 这就表示,直接实例test所属的类Test去访问不存在的属性的时候是走不到这个方法里的,同理,如果给实例test.__dict__添加一个getattr方法,但是test所属的Test类是没有getattr方法的,这时候test去访问不存在的属性 也会报错

    class Test:
        pass
    test = Test()
    test.__getattr__ = lambda attr_name:"固定返回内容"
    print(test.xx) #报错没有该属性
    
  • 相关阅读:
    算法--判断数组中是否有重复值
    算法--小范围排序
    Spark性能调优之JVM调优
    算法-java代码实现基数排序
    算法-java代码实现计数排序
    算法-java代码实现希尔排序
    算法-java代码实现堆排序
    Kafka集群的搭建
    深度学习必备包
    Keras 学习之旅(一)
  • 原文地址:https://www.cnblogs.com/alantammm/p/15772061.html
Copyright © 2020-2023  润新知