1. 引言
Python 的灵活性不仅体现在语法层面,更来源于其高度动态的对象模型与统一的属性访问机制。相比于仅停留在"如何使用类与对象",深入理解其底层运行原理,对于掌握 Django、ORM 乃至框架设计具有重要意义。
本文将围绕两个核心问题展开:
- Python 对象是如何创建的?
- Python 属性是如何被访问和控制的?
2. Python 对象模型
-
类的本质
在 Python 中,类并不是一种特殊结构,而是一个普通对象,其创建由内置元类
type完成。pythonclass A: x = 10等价于:
pythonA = type("A", (), {"x": 10})由此可以得到:
- 类是
type的实例 type本身也是type的实例(自举结构)
pythontype(A) is type type(type) is type这一机制使 Python 具备极强的动态构造能力。
- 类是
-
对象创建流程
对象的创建本质上是对"类对象"的一次调用:
pythona = A()在 Python 内部,该过程并非简单实例化,而是经历如下调用链:
textA() ↓ type.__call__(A) ↓ 1️⃣ 调用 A.__new__() → 创建实例对象 ↓ 2️⃣ 调用 A.__init__() → 初始化实例 ↓ 返回实例需要注意:
- 类之所以可以被调用,是因为其元类
type实现了__call__ __new__负责创建对象__init__负责初始化对象
在一般开发中无需直接操作
__call__,但该机制在元类与框架设计中具有重要作用。 - 类之所以可以被调用,是因为其元类
-
属性存储机制
Python 使用字典结构管理对象属性:
- 实例属性:
obj.__dict__ - 类属性:
class.__dict__
示例:
pythonclass A: x = 10 a = A()此时:
pythona.__dict__ # {} A.__dict__ # 包含 'x': 10当执行:
pythona.x = 20本质为:
pythona.__dict__['x'] = 20 - 实例属性:
-
属性查找规则
访问属性:
pythona.x默认查找顺序为:
text1️⃣ 实例字典 obj.__dict__ 2️⃣ 类字典 class.__dict__ 3️⃣ 父类链(MRO)示例:
pythonclass A: x = 10 a = A() a.x = 20优先返回实例属性 20。
若执行:
pythondel a.x则回退至类属性,返回 10。
3. 属性访问机制
-
统一入口:getattribute
在 Python 中,所有属性访问都会统一转化为:
pythona.x → a.__getattribute__("x")因此可以认为:
__getattribute__是属性访问的总入口示例:
pythonclass A: def __getattribute__(self, name): print("访问", name) return super().__getattribute__(name) -
兜底机制:getattr
当属性在正常查找流程中未找到时,Python 会触发:
python__getattr__(self, name)示例:
pythonclass A: def __getattr__(self, name): return 999 a = A() a.x # 返回 999 -
属性访问完整流程
属性访问的完整流程如下:
texta.x ↓ 1️⃣ 调用 __getattribute__("x")(必定执行) ↓ 2️⃣ 若查找成功 → 直接返回 ↓ 3️⃣ 若抛出 AttributeError ↓ 4️⃣ 调用 __getattr__("x")关键区别如下:
方法 执行时机 作用 __getattribute__必定执行 控制所有属性访问 __getattr__查找失败后 提供兜底逻辑 -
异常驱动机制
需要特别强调:
__getattr__的触发依赖于AttributeError例如:
pythondef __getattribute__(self, name): return 100此时不会触发
__getattr__,因为属性访问并未失败。 -
方法访问的本质
在 Python 中,方法本质上也是属性:
pythonself.__getattr__等价于:
pythonself.__getattribute__("__getattr__")因此,在调试或日志中可能会看到:
text访问 __getattr__ -
递归风险与正确写法
在重写
__getattribute__时,如果直接访问属性,容易导致无限递归:错误示例:
pythondef __getattribute__(self, name): return self.x其执行过程为:
textself.x → __getattribute__("x") → self.x → ...最终触发
RecursionError。正确写法应为:
pythonreturn super().__getattribute__(name) -
调试环境的特殊行为
在调试工具(如 PyCharm Debug)中,可能出现如下输出:
text访问 __class__ 访问 __class__ ...原因在于调试器会主动访问对象属性以获取类型与结构信息,例如:
pythonobj.__class__这些访问同样会触发
__getattribute__。需要明确:
这些行为来源于调试工具,而非程序本身逻辑。
4. 整体模型总结
-
对象关系结构
texttype → 类 → 实例type负责创建类- 类负责创建实例
-
属性访问模型
texta.x ↓ __getattribute__ ↓ (内部查找) obj.__dict__ class.__dict__ 父类链 ↓ 查找失败 → __getattr__
5. 工程意义
上述机制在实际工程中具有直接应用价值:
| 机制 | 应用场景 |
|---|---|
__getattribute__ |
日志系统、权限控制 |
__getattr__ |
安全访问、默认值处理 |
| 属性查找链 | ORM 字段解析 |
type / 元类 |
模型定义、自动注册 |
例如:
- Django ORM 的字段访问机制
- DRF 的序列化流程
- 权限控制与中间件设计
这些框架能力,本质上均建立在 Python 对象模型之上。
6. 总结
Python 的核心机制可以归纳为两部分:
text
对象如何被创建(type / __new__ / __init__)
属性如何被访问(__getattribute__ / __getattr__)
进一步抽象为:
text
Python = 对象模型 + 属性访问控制机制
理解这一点,意味着从"使用 Python"迈向"理解 Python",也是深入框架源码与系统设计的关键一步。