Python属性查找顺序为:先实例字典→再按MRO遍历类字典→描述符协议介入→特殊方法绕过常规流程。实例属性遮蔽类属性;MRO决定多重继承中同名属性优先级;描述符(如@property)可定制访问逻辑;特殊方法需在类中定义。

Python中对象属性查找遵循明确的顺序规则,核心是MRO(Method Resolution Order)和描述符协议共同作用的结果。理解这个顺序,能避免属性访问异常、正确覆盖父类行为,并写出更可靠的面向对象代码。
实例字典优先于类字典
访问对象属性时,Python首先在实例的__dict__中查找。如果找到,直接返回值,不再继续搜索。这解释了为什么给实例动态赋值同名属性会“遮蔽”类属性或父类方法。
- 例如:
obj.x = 10后,obj.x始终返回10,即使类中定义了x = 20或父类有x方法 - 删除实例属性:
del obj.x后,再次访问obj.x将按后续规则继续查找
按MRO顺序遍历类及其父类
实例字典未命中时,Python按该对象所属类的MRO序列(从左到右、深度优先但C3线性化)依次在每个类的__dict__中查找。MRO可通过ClassName.__mro__或ClassName.mro()查看。
- 多重继承下,MRO确保每个父类只被搜索一次,且子类总在父类之前
- 若多个父类定义同名属性,靠前的类中定义的版本生效(非“最近父类”,而是MRO中位置最前)
- 注意:类变量和实例方法都走此路径;但方法调用会额外触发绑定(bound method)机制
描述符协议介入属性访问
当在类字典中查到一个对象,且该对象实现了__get__(以及可选__set__/__delete__),它就是一个描述符。此时Python不直接返回该对象,而是调用其__get__(instance, owner)方法——这意味着属性行为可被完全定制。
立即学习“Python免费学习笔记(深入)”;
- 函数、property、classmethod、staticmethod 都是内置描述符
-
@property修饰的方法本质是data descriptor(有__set__),优先级高于实例字典中的同名属性 - 非数据描述符(只定义
__get__)优先级低于实例字典,但高于普通类属性
特殊方法名绕过常规查找
以双下划线开头和结尾的特殊方法(如__len__、__add__)不完全遵循上述顺序。Python在特定操作(如len(obj))中,会直接在对象的类(而非实例)中查找,且不触发描述符协议(即不会调用__get__)。
- 因此,在实例上设置
obj.__len__ = lambda: 42对len(obj)无效 - 重载运算符必须在类定义中实现,不能靠实例赋值覆盖
- 例外:
__getattr__仅在常规查找全部失败后才被调用,用于兜底处理;而__getattribute__则拦截所有属性访问(慎用)










