Python 对象模型与属性访问机制

1. 引言

Python 的灵活性不仅体现在语法层面,更来源于其高度动态的对象模型与统一的属性访问机制。相比于仅停留在"如何使用类与对象",深入理解其底层运行原理,对于掌握 Django、ORM 乃至框架设计具有重要意义。

本文将围绕两个核心问题展开:

  • Python 对象是如何创建的?
  • Python 属性是如何被访问和控制的?

2. Python 对象模型

  1. 类的本质

    在 Python 中,类并不是一种特殊结构,而是一个普通对象,其创建由内置元类 type 完成。

    python 复制代码
    class A:
        x = 10

    等价于:

    python 复制代码
    A = type("A", (), {"x": 10})

    由此可以得到:

    • 类是 type 的实例
    • type 本身也是 type 的实例(自举结构)
    python 复制代码
    type(A) is type
    type(type) is type

    这一机制使 Python 具备极强的动态构造能力。

  2. 对象创建流程

    对象的创建本质上是对"类对象"的一次调用:

    python 复制代码
    a = A()

    在 Python 内部,该过程并非简单实例化,而是经历如下调用链:

    text 复制代码
    A()
    ↓
    type.__call__(A)
    ↓
    1️⃣ 调用 A.__new__()
       → 创建实例对象
    ↓
    2️⃣ 调用 A.__init__()
       → 初始化实例
    ↓
    返回实例

    需要注意:

    • 类之所以可以被调用,是因为其元类 type 实现了 __call__
    • __new__ 负责创建对象
    • __init__ 负责初始化对象

    在一般开发中无需直接操作 __call__,但该机制在元类与框架设计中具有重要作用。

  3. 属性存储机制

    Python 使用字典结构管理对象属性:

    • 实例属性:obj.__dict__
    • 类属性:class.__dict__

    示例:

    python 复制代码
    class A:
        x = 10
    
    a = A()

    此时:

    python 复制代码
    a.__dict__   # {}
    A.__dict__   # 包含 'x': 10

    当执行:

    python 复制代码
    a.x = 20

    本质为:

    python 复制代码
    a.__dict__['x'] = 20
  4. 属性查找规则

    访问属性:

    python 复制代码
    a.x

    默认查找顺序为:

    text 复制代码
    1️⃣ 实例字典 obj.__dict__
    2️⃣ 类字典 class.__dict__
    3️⃣ 父类链(MRO)

    示例:

    python 复制代码
    class A:
        x = 10
    
    a = A()
    a.x = 20

    优先返回实例属性 20。

    若执行:

    python 复制代码
    del a.x

    则回退至类属性,返回 10。

3. 属性访问机制

  1. 统一入口:getattribute

    在 Python 中,所有属性访问都会统一转化为:

    python 复制代码
    a.x  →  a.__getattribute__("x")

    因此可以认为:

    __getattribute__ 是属性访问的总入口

    示例:

    python 复制代码
    class A:
        def __getattribute__(self, name):
            print("访问", name)
            return super().__getattribute__(name)
  2. 兜底机制:getattr

    当属性在正常查找流程中未找到时,Python 会触发:

    python 复制代码
    __getattr__(self, name)

    示例:

    python 复制代码
    class A:
        def __getattr__(self, name):
            return 999
    
    a = A()
    a.x  # 返回 999
  3. 属性访问完整流程

    属性访问的完整流程如下:

    text 复制代码
    a.x
    ↓
    1️⃣ 调用 __getattribute__("x")(必定执行)
    ↓
    2️⃣ 若查找成功 → 直接返回
    ↓
    3️⃣ 若抛出 AttributeError
    ↓
    4️⃣ 调用 __getattr__("x")

    关键区别如下:

    方法 执行时机 作用
    __getattribute__ 必定执行 控制所有属性访问
    __getattr__ 查找失败后 提供兜底逻辑
  4. 异常驱动机制

    需要特别强调:

    __getattr__ 的触发依赖于 AttributeError

    例如:

    python 复制代码
    def __getattribute__(self, name):
        return 100

    此时不会触发 __getattr__,因为属性访问并未失败。

  5. 方法访问的本质

    在 Python 中,方法本质上也是属性:

    python 复制代码
    self.__getattr__

    等价于:

    python 复制代码
    self.__getattribute__("__getattr__")

    因此,在调试或日志中可能会看到:

    text 复制代码
    访问 __getattr__
  6. 递归风险与正确写法

    在重写 __getattribute__ 时,如果直接访问属性,容易导致无限递归:

    错误示例:

    python 复制代码
    def __getattribute__(self, name):
        return self.x

    其执行过程为:

    text 复制代码
    self.x → __getattribute__("x") → self.x → ...

    最终触发 RecursionError

    正确写法应为:

    python 复制代码
    return super().__getattribute__(name)
  7. 调试环境的特殊行为

    在调试工具(如 PyCharm Debug)中,可能出现如下输出:

    text 复制代码
    访问 __class__
    访问 __class__
    ...

    原因在于调试器会主动访问对象属性以获取类型与结构信息,例如:

    python 复制代码
    obj.__class__

    这些访问同样会触发 __getattribute__

    需要明确:

    这些行为来源于调试工具,而非程序本身逻辑。

4. 整体模型总结

  1. 对象关系结构

    text 复制代码
    type → 类 → 实例
    • type 负责创建类
    • 类负责创建实例
  2. 属性访问模型

    text 复制代码
    a.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",也是深入框架源码与系统设计的关键一步。

相关推荐
xyq20243 小时前
Swift 下标脚本
开发语言
weixin_402486343 小时前
小分子 pdb准化为sdf
python
橘子编程3 小时前
密码学完全指南:从基础到实战
java·python·密码学
蓝色的杯子3 小时前
Python面试30分钟突击掌握-LeetCode2-Strings
python
计算机安禾3 小时前
【数据结构与算法】第44篇:堆(Heap)的实现
c语言·开发语言·数据结构·c++·算法·排序算法·图论
ZC跨境爬虫3 小时前
海南大学交友平台开发实战 day9(头像上传存入 SQLite+BLOB 存储 + 前后端联调避坑全记录)
前端·数据库·python·sqlite
FreakStudio3 小时前
嘉立创开源:应该是全网MicroPython教程最多的开发板
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy
kinl20183 小时前
cs2385_note5 (lec18-lec19) Variational Inference & Control as Inference
笔记
上天_去_做颗惺星 EVE_BLUE3 小时前
接口自动化测试全流程:pytest 用例收集、并行执行、Allure 报告合并与上传
python·pytest