Python属性查找链
Python属性查找遵循严格的链式顺序,理解这一机制对掌握元编程至关重要。
查找顺序
属性查找按以下优先级进行:
- 数据描述符(类中定义了
__get__和__set__的描述符) - 实例字典(
instance.__dict__) - 非数据描述符(仅定义
__get__的描述符) - 类字典(
type(instance).__dict__) - 基类字典(沿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__()
要点总结
- 数据描述符优先级最高,覆盖实例属性
- 实例字典优先于非数据描述符和类属性
- MRO决定基类查找顺序(C3线性化算法)
- **
__getattribute__**拦截所有访问,__getattr__仅处理缺失属性 - 描述符必须定义在类层级,实例层级无效
📝 发现内容有误?点击此处直接编辑