第8.33节 Python中__getattr__以及__getattr__与__ getattribute__的关系深入剖析

一、 引言
前面几节分别介绍了Python中属性操作捕获的三剑客:__ getattribute__方法、__setattr__方法、__delattr__方法,为什么__ getattribute__方法与后两者的命名规则会不同呢?为什么属性读取的方法不是__ getattr__方法呢?这是因为Python中__ getattr__方法别有用途。

二、 __getattr__与__getattribute__关系
__getattr__是Python中的内置函数,该函数可以在实例的属性查看时出现异常时执行,object类的__getattr__直接抛出异常。
__getattr__与__getattribute__都是内置方法,都是与属性查看相关的方法,当Python每次调用一个实例的属性查看时,Python会无条件进入__getattribute__中,而当对应属性不存在时,Python就会调用__getattr__来进行处理。

  1. 当默认属性访问因引发 AttributeError 而失败时__getattr__被调用,这种调用:
    1)可能是调用 __getattribute__() 时由于 name 不是一个实例属性或 self 的类关系树中的属性而引发了 AttributeError
    2)可能是对 name 特性属性调用 __get__() 时引发了 AttributeError
  2. 此方法应当返回(找到的)属性值或是引发一个 AttributeError 异常。如果属性是通过正常机制找到的,__getattr__() 就不会被调用;
  3. 重写__getattr__()时,如果重写的自定义方法不能在自定义代码中返回对应值或抛出异常,则可以调用父类的__getattr__()方法。

三、 为什么有__getattribute__还需要__getattr__
通过前面的介绍,我们知道__getattr__执行时,__getattribute__肯定会执行,__getattribute__执行时,不一定会执行__getattr__。那为什么还需要__getattr__呢?
这是Python在 __getattr__() 和 __setattr__() 之间故意设置的不对称性:

  1. 这是为了提高处理效率,__getattribute__不论什么情况查看属性都会执行,而 __getattr__只在属性错误的情况下执行,如果业务逻辑只要求错误情况下才进行捕获处理,则__getattribute__重写会导致访问效率降低;
  2. 如果出错情况下调用__getattribute__,而在处理逻辑中为了支持这种异常情况的处理可能会需要访问实例的其他属性,这会导致再次触发__getattribute__,从而可能导致嵌套调用甚至无穷尽的调用,而使用__getattr__则可以避免这种情况

四、 案例

  1. 案例说明
    案例中定义了类Car,类中有构造方法、__getattribute__和__getattr__方法,用该类定义实例对象car,通过实例访问正常的实例变量和不存在的方法、不存在的实例变量,看__getattribute__和__getattr__方法的执行情况。
  2. 源代码
>>> car = Car('汽油发动机')
>>> car.power #存在的属性查看只调用了__getattribute__
execute __getattribute__power
>>> car.drive() #一个不存在的方法drive调用了__getattribute__,同时还调用了__getattr__,最后抛出AttributeError异常
execute __getattribute__drive
execute __getattr__:name=drive
Traceback (most recent call last):
  File "<pyshell#50>", line 1, in <module>
    car.drive() #一个不存在的方法drive调用了__getattribute__,同时还调用了__getattr__,最后抛出AttributeError异常
  File "<pyshell#47>", line 12, in __getattr__
    super().__getattr__(name)
AttributeError: 'super' object has no attribute '__getattr__'
>>> car.Power #一个不存在的实例变量Power调用了__getattribute__,同时还调用了__getattr__,最后抛出AttributeError异常
execute __getattribute__Power
execute __getattr__:name=Power
Traceback (most recent call last):
  File "<pyshell#51>", line 1, in <module>
    car.Power #一个不存在的实例变量Power调用了__getattribute__,同时还调用了__getattr__,最后抛出AttributeError异常
  File "<pyshell#47>", line 12, in __getattr__
    super().__getattr__(name)
AttributeError: 'super' object has no attribute '__getattr__'
>>>
  1. 执行截图
    在这里插入图片描述

本节介绍了__getattr__方法触发的场景,并与__getattribute__方法进行了比对,开发者可以在特定情况下重写该方法对抛出异常进行截获,如特定条件下网络访问URL的重定向。
老猿Python,跟老猿学Python!
博客地址:https://blog.csdn.net/LaoYuanPython

请大家多多支持,点赞、评论和加关注!谢谢!

原文地址:https://www.cnblogs.com/LaoYuanPython/p/13643700.html