Python的类中,属性分为实例属性和类属性。通过实例可以访问实例和类属性,通过类名只能访问类属性。下面分别就这两种方式来给出属性的访问流程。
测试代码:
class C(object):
a = 'abc'
def __getattribute__(self, item):
print("__getattribute__ called.")
return object.__getattribute__(self, item)
def __getattr__(self, item):
print("__getattr__ called.")
return item + "from getattr"
def __get__(self, instance, owner): # 实现了__get__函数,说明C的实例是描述符
print("__get__ called. ", end=',')
print(instance, end=',')
print(owner, end=',')
print(self)
return self
def foo(self, x):
print(x)
class C2(object):
d = C() # 描述符d只有作为类属性才有效
def main():
e = C()
f = C2()
print(e.a) # 实例访问有效属性流程:__getattribute__
print(e.x) # 实例访问无效属性流程:__getattribute__->__getattr__
print(f.d.a) # 对描述符d的访问,都要先访问__get__,流程:__get__->__getattribute__->__getattr__
print(C.a) # 类访问有效属性:直接访问
print(C.x) # 类访问无效属性:直接抛出异常
print(C2.d.x) # 类访问描述符访问无效属性:__get__->__getattribute__->__getattr__
if __name__ == "__main__":
main()
对上述代码分析如下:
1、类C定义了类属性a,实例方法foo,重写了3个魔法方法 __getattribute__、__getattr__和__get__。由于C重写了__get__方法,根据Python的定义,类C属于描述符。类C2定义了类属性d,只有当描述符对象d作为类属性的时候,描述符才有效。
2、继承关系:C和C2均继承object类,属于新式类。
3、通过实例对象访问实例属性或类属性的时候,如e.a,流程如下:
(1) 获取e的类C的mro顺序:Class C--->Class objectc,
(2)从左至右开始查找mro列表,首先找到类C,
(3)调用类C的魔法方法__getattribute__,此时就能找到属性a,因此不再继续进行后续查找。若此时调用该魔法方法没有找到属性,则继续调用魔法__getattr__,找到则返回,没找到则继续在mro中下一个类的重复上述过来。若mro查找完后仍然没找到,就会抛出异常
4、当类属性是一个描述符时,访问该属性时,必须在调用魔法方法__getattribute__前面调用__get__方法