3.2.6 实例属性的查找顺序

我们知道类的属性都会存储在__dict__ 字典中,即使没有显式的给属性赋值,但只要字典里面有这个字段,也是可以读取到的:

class Person:
    pass

p = Person()
p.__dict__['name'] = 'JarvisMa'
p.name  # 不会报错,而是返回字典中的值,'JarvisMa'

但我们在特性工厂和属性描述符的实现中,都是直接把属性的值存储在 __dict__ 中,而且键就是属性名。之前我们还介绍过,特性的工作原理是没有直接访问实例的属性,而是读取了 property 的实例。那直接把值存在 __dict__ 中,会不会导致特性失效,直接访问到原始内容呢?从之前的实践结果来看,答案是否定的,要解释这个问题,我们需要搞明白访问实例属性的查找顺序。

假设有这么一段代码:

o = cls()   # 假设 o 是 cls 类的实例
o.attr      # 试图访问 o 的属性 attr

再对上一节中的属性描述符做一个简单的分类:

  1. 覆盖型描述符:定义了 __set__ 方法的描述符

  2. 非覆盖型描述符:没有定义 __set__方法的描述符

在执行 o.attr 时,查找顺序如下:

  1. 如果 attr 出现在 cls 或父类的 __dict__ 中,且 attr 是覆盖型描述符,那么调用 __get__ 方法。

  2. 否则,如果 attr 出现在 o__dict__ 中,返回 o.__dict__[attr]

  3. 否则,如果 attr 出现在 cls 或父类的 __dict__中,如果 attr 是非覆盖型描述符,那么调用 __get__ 方法。

  4. 否则,如果没有非覆盖型描述符,直接返回 cls.__dict__[attr]

  5. 否则,如果 cls 实现了 __getattr__方法,调用这个方法

  6. 抛出 AttributeError

所以,在访问类的属性时,覆盖型描述符的优先级是高于直接存储在 __dict__ 中的值的。

Last updated