Python 高级编程 020:属性查找全解析
- [一、先搞懂:类属性 VS 实例属性](#一、先搞懂:类属性 VS 实例属性)
-
- [1. 核心概念](#1. 核心概念)
- [2. 直观代码演示](#2. 直观代码演示)
- 二、关键规则:属性查找「由下而上」
- [三、进阶难点:多继承与 MRO 算法演变](#三、进阶难点:多继承与 MRO 算法演变)
-
- [1. 初代:深度优先搜索(Python 2.2 前 经典类)](#1. 初代:深度优先搜索(Python 2.2 前 经典类))
- [2. 迭代:广度优先搜索(Python 2 经典类优化)](#2. 迭代:广度优先搜索(Python 2 经典类优化))
- [3. 最终版:C3 算法(Python 2.3 至今 → Python 3 统一)](#3. 最终版:C3 算法(Python 2.3 至今 → Python 3 统一))
- [四、实战技巧:快速查看 MRO 查找顺序](#四、实战技巧:快速查看 MRO 查找顺序)
-
- [1. 菱形继承 MRO 演示](#1. 菱形继承 MRO 演示)
- [2. 普通多继承 MRO 演示](#2. 普通多继承 MRO 演示)
- [五、关键补充:Python 3 新式类特性](#五、关键补充:Python 3 新式类特性)
- [六、总结:3 句话牢记属性查找](#六、总结:3 句话牢记属性查找)
在 Python 面向对象编程的世界里,类属性 与实例属性 是构建程序的基石,而它们的查找顺序(MRO) 更是决定代码行为的核心逻辑。很多开发者在单继承中得心应手,却在多继承、菱形继承场景下遭遇属性查找异常,本质是对属性查找规则与算法演变理解不透彻。今天,我们就从基础概念到底层算法,彻底吃透 Python 属性查找的奥秘✨。
一、先搞懂:类属性 VS 实例属性
在 Python 中,类 和实例(对象) 都拥有独立的属性空间,二者的属性定义、存储位置、访问逻辑完全不同。
1. 核心概念
-
属性:类内部定义的变量、方法,统称为属性;
-
类属性 :定义在类体内、方法外的属性,归类本身所有,所有实例共享;
-
实例属性 :通过
self.属性名在__init__方法中初始化的属性,归单个实例独有,互不干扰。
2. 直观代码演示
python
# 定义类,包含类属性
class A:
# 🔥 类属性:属于类 A,所有实例共享
name = "类A的属性"
def __init__(self):
# 🔥 实例属性:属于当前实例,独立存储
self.name = "实例的属性"
# 实例化对象
obj = A()
# 优先访问实例属性
print(obj.name) # 输出:实例的属性
# 访问类属性(类名调用)
print(A.name) # 输出:类A的属性
二、关键规则:属性查找「由下而上」
访问实例的属性时,Python 遵循先实例、后类 的由下而上查找顺序,这是最基础的核心规则:
-
第一步:优先查找实例自身的属性空间;
-
第二步:若实例无该属性,自动向上查找所属类的属性空间;
-
第三步:类中仍无,则继续向上查找父类,直至顶层
object。
简单来说:实例属性会「覆盖」同名类属性,类属性是实例属性的「兜底」。
三、进阶难点:多继承与 MRO 算法演变
单继承的属性查找简单易懂,但多继承 (一个类继承多个父类)会让查找逻辑指数级复杂。Python 为了解决这个问题,历经了深度优先 → 广度优先 → C3 算法 的三次迭代,最终确定了现在的 MRO(Method Resolution Order,方法解析顺序) 规则。
1. 初代:深度优先搜索(Python 2.2 前 经典类)
深度优先的逻辑:沿着一条继承链深挖到底,再切换下一条链。
-
适用场景:非菱形的普通多继承;
-
致命缺陷:菱形继承失效!
❌ 菱形继承问题:
A 继承 B、C → B、C 都继承 D,若 C 重写了 D 的方法,深度优先会先查 B→D,直接跳过 C,导致 C 的重写方法永远无法生效,违背面向对象的重载逻辑。
2. 迭代:广度优先搜索(Python 2 经典类优化)
广度优先的逻辑:先查完同一层级的所有父类,再向上查顶层父类。
-
解决了菱形继承的覆盖问题;
-
新缺陷:非菱形多继承时,会破坏「先继承的父类优先级更高」的规则,导致父类方法被意外覆盖。
3. 最终版:C3 算法(Python 2.3 至今 → Python 3 统一)
从 Python 2.3 开始,官方彻底抛弃深度 / 广度优先,采用C3 算法统一属性查找规则,也是 Python 3 新式类的唯一标准算法。
-
优势:兼顾菱形继承、多继承的优先级,保证查找顺序稳定、可预测;
-
特点:算法公式复杂,日常开发无需深究原理,只需学会查看 MRO 顺序即可。
四、实战技巧:快速查看 MRO 查找顺序
Python 提供了内置属性 __mro__,可以直接打印类的属性查找完整顺序,这是排查多继承问题的神器🚀。
1. 菱形继承 MRO 演示
python
# 菱形继承结构:D → B、C → A
class D:
name = "D类"
class B(D):
pass
class C(D):
name = "C类"
class A(B, C):
pass
# 打印 MRO 查找顺序
print(A.__mro__)
# 输出:(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
✅ 查找顺序:A → B → C → D → object,完美解决菱形继承的覆盖问题。
2. 普通多继承 MRO 演示
python
# 继承结构:D→B、E→C → A(B,C)
class D:
name = "D类"
class E:
name = "E类"
class B(D):
pass
class C(E):
pass
class A(B, C):
pass
# 打印 MRO 查找顺序
print(A.__mro__)
# 输出:(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
✅ 查找顺序:A → B → D → C → E → object,严格遵循继承优先级。
五、关键补充:Python 3 新式类特性
Python 3 中所有类默认都是新式类 ,无论是否显式书写,都会自动继承顶层基类 object,这也是 MRO 算法能稳定运行的基础:
-
无需手动写
class A(object):,简化代码; -
统一属性查找规则,告别经典类的兼容问题。
六、总结:3 句话牢记属性查找
-
单继承:实例优先,类兜底,逐级向上;
-
多继承:Python 3 统一用 C3 算法 ,靠
__mro__查顺序; -
菱形继承:C3 算法保证子类重写优先,父类兜底。

吃透这些规则,无论是类属性、实例属性的定义,还是多继承下的属性冲突排查,都能轻松拿捏,写出更稳健的 Python 面向对象代码💻。