python 在访问属性的方法上定义了__getattr__()
和 __getattribute__()
2种方法,其区别非常细微,但非常重要。
- 如果某个类定义了
__getattribute__()
方法,在 每次引用属性或方法名称时 Python 都调用它(特殊方法名称除外,因为那样将会导致讨厌的无限循环)。 - 如果某个类定义了
__getattr__()
方法,Python 将只在正常的位置查询属性时才会调用它。如果实例x
定义了属性color
,x.color
将 不会 调用x.__getattr__('color')
;而只会返回x.color
已定义好的值。
下边举几个栗子:
当一个类没有定义__getattr__
和__getattribute__
时,在访问类的实例一个不存在的属性时会报错
1 | class GetAttrClass(object): |
上边程序运行后得到一下错误:
1 | Traceback (most recent call last): |
当一个类定义__getattr__
或__getattribute__
时,在访问类的实例一个不存在的属性时会返回None
1 | class GetAttrClass(object): |
程序运行结果为:None
当存在已定义好的值后,不再调用__getattr__
而是直接返回定义好的值
1 | class GetAttrClass(object): |
程序运行结果为:
1 | red |
1 | class GetAttrClass(object): |
程序运行结果为:
1 | black |
当程序定义__getattribute__
后,每次引用属性和方法都会调用它
1 | class GetAttrClass(object): |
程序运行结果为:
1 | red |
即便已经显式地设置 gac.color,在获取 gac.color 的值时, 仍将调用 __getattribute__()
方法。如果存在 __getattribute__()
方法,将在每次查找属性和方法时 无条件地调用 它,哪怕在创建实例之后已经显式地设置了属性。
如果定义了类的
__getattribute__()
方法,你可能还想定义一个__setattr__()
方法,并在两者之间进行协同,以跟踪属性的值。否则,在创建实例之后所设置的值将会消失在黑洞中。
必须特别小心 __getattribute__()
方法,因为 Python 在查找类的方法名称时也将对其进行调用。
1 | class GetAttrClass(object): |
以上程序报错:
1 | Traceback (most recent call last): |
- 该类定义了一个总是引发 AttributeError 异常的
__getattribute__()
方法。没有属性或方法的查询会成功。 - 调用
gac.hello()
时,Python 将在 GetAttrClass 类中查找hello()
方法。该查找将执行整个__getattribute__()
方法,因为所有的属性和方法查找都通过__getattribute__()
方法。在此例中,__getattribute__()
方法引发 AttributeError 异常,因此该方法查找过程将会失败,而方法调用也将失败。