全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-19 8 分钟 ✍️ juanwangdev

Python属性查找链

Python属性查找遵循严格的链式顺序,理解这一机制对掌握元编程至关重要。

查找顺序

属性查找按以下优先级进行:

  1. 数据描述符(类中定义了__get____set__的描述符)
  2. 实例字典instance.__dict__
  3. 非数据描述符(仅定义__get__的描述符)
  4. 类字典type(instance).__dict__
  5. 基类字典(沿MRO链查找)

核心机制演示

Python
# 描述符类型对比
class DataDescriptor:
    "数据描述符:优先级高于实例字典"
    def __get__(self, obj, owner):
        return f"DataDescriptor.get from {obj}"

    def __set__(self, obj, value):
        print(f"DataDescriptor.set: {value}")

class NonDataDescriptor:
    "非数据描述符:优先级低于实例字典"
    def __get__(self, obj, owner):
        return f"NonDataDescriptor.get from {obj}"

class Demo:
    data_desc = DataDescriptor()
    non_data_desc = NonDataDescriptor()
    class_attr = "class value"

# 测试查找顺序
d = Demo()

# 数据描述符优先于实例字典
d.__dict__['data_desc'] = "instance override"
print(d.data_desc)  # 输出: DataDescriptor.get from <Demo object>

# 实例字典优先于非数据描述符
d.__dict__['non_data_desc'] = "instance override"
print(d.non_data_desc)  # 输出: instance override

MRO与基类查找

Python
class A:
    attr = "A"

class B(A):
    pass

class C(A):
    attr = "C"

class D(B, C):
    pass

# MRO决定基类查找顺序
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
print(D.attr)  # 输出: C(按MRO顺序,C在A之前)

__getattr____getattribute__

Python
class CompleteLookup:
    def __getattribute__(self, name):
        "拦截所有属性访问"
        print(f"__getattribute__ called for {name}")
        return super().__getattribute__(name)

    def __getattr__(self, name):
        "仅在属性不存在时调用"
        print(f"__getattr__ called for {name}")
        return f"fallback: {name}"

obj = CompleteLookup()
obj.existing = "value"
print(obj.existing)  # __getattribute__ → 返回 "value"
print(obj.missing)   # __getattribute__ → __getattr__ → "fallback: missing"

注意:__getattribute__极易造成无限递归,必须用super().__getattribute__()访问属性。

完整查找流程图

text
属性访问 obj.attr
        ↓
┌───────────────────────────────┐
│ __getattribute__(可拦截)      │
└───────────────────────────────┘
        ↓
  类中是否有数据描述符?
        ↓是                    ↓否
返回描述符.__get__()     检查实例.__dict__
                              ↓
                        找到? → 返回值
                              ↓未找到
                     类中是否有非数据描述符?
                              ↓是          ↓否
                     返回描述符.__get__()  检查类.__dict__
                                              ↓
                                        沿MRO查找基类
                                              ↓
                                          找到?→返回
                                              ↓未找到
                                        调用__getattr__()

要点总结

  1. 数据描述符优先级最高,覆盖实例属性
  2. 实例字典优先于非数据描述符和类属性
  3. MRO决定基类查找顺序(C3线性化算法)
  4. **__getattribute__**拦截所有访问,__getattr__仅处理缺失属性
  5. 描述符必须定义在类层级,实例层级无效

📝 发现内容有误?点击此处直接编辑

← 上一篇 Python导入系统实现
下一篇 → Python方法调用机制
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库