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