登录
首页 >  文章 >  python教程

Python类与对象内存结构解析

时间:2026-03-31 23:33:24 393浏览 收藏

Python类与对象的内存结构远非简单容器,而是由PyObject头(含引用计数与类型指针)、动态属性存储机制(默认通过__dict__字典)及运行时方法绑定三者精密协同构成的灵活系统;理解它,就能揭开属性查找、方法调用开销、内存占用差异背后的本质——从普通实例的字典式动态扩展,到__slots__带来的结构化内存布局与性能跃升,每一步都体现着Python在动态性与效率之间的精妙平衡。

Python类与对象内存结构_对象模型解析

Python 中的类和对象在内存中并不是简单的数据容器,而是由一套精巧的对象模型支撑的动态结构。理解其内存布局,关键不在于“地址”或“字节”,而在于 类型指针、属性存储方式、方法绑定机制 三者的协同关系。

对象底层:PyObject + 实际数据

每个 Python 对象(如 a = 3obj = MyClass())在 C 层都对应一个 PyObject 结构体,它包含两个核心字段:

  • ob_refcnt:引用计数,用于垃圾回收
  • ob_type:指向该对象所属类型的指针(例如 intdict 或自定义类的 PyTypeObject

真正存放数据的部分(如整数的值、列表的元素指针数组、实例的属性字典)则紧跟在 PyObject 之后,由具体类型决定布局。比如 int 对象额外存一个 ob_digit 数组,而用户定义的实例默认只预留一个指向 __dict__ 的指针位置。

实例对象的属性去哪儿了?

普通实例(未使用 __slots__)的属性全部存在一个独立的字典对象中,即 obj.__dict__。这个字典本身也是一个 Python 对象,有自己的 PyObject 头和哈希表结构。

  • 执行 obj.x = 10,实际是往 obj.__dict__ 这个字典里插入键值对 'x': 10
  • 访问 obj.x 时,解释器先查 obj.__dict__;没找到再沿 obj.__class__.__mro__ 查类和父类的 __dict__
  • 因此,实例属性和类属性物理上完全分离——类属性存在类对象的 __dict__ 中,不占用每个实例的内存

方法调用不是“复制代码”,而是创建绑定方法对象

当你写 obj.method(),Python 并没有把函数体拷进对象里。整个过程分三步:

  • obj.__class__ 开始,在 MRO 链上查找名为 method 的属性
  • 如果找到的是一个函数对象(function),就用 obj 和该函数构造一个新的 method 对象(即绑定方法)
  • 调用时,这个绑定方法自动把 obj 作为第一个参数(self)传给原函数

也就是说,方法调用开销主要来自“查找 + 绑定”这两步,而不是存储冗余代码。这也是为什么给实例动态添加方法(如 obj.f = lambda: ...)后,它不会自动获得 self——因为那只是一个普通函数赋值,未经过绑定逻辑。

__slots__ 改变了什么?

当类定义了 __slots__ = ['x', 'y'],Python 就不再为每个实例创建 __dict__ 字典,而是直接在实例内存块中按顺序分配固定字段(类似 C struct)。

  • 节省内存:避免字典哈希表的额外开销(约 240 字节起步)
  • 加速属性访问:跳过字典查找,直接通过偏移量读取字段
  • 限制灵活性:不能动态添加未在 __slots__ 中声明的属性,也不能再有 __dict__

注意:__slots__ 只作用于定义它的类的**直接实例**,子类除非也定义 __slots__,否则仍会拥有 __dict__

文中关于的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《Python类与对象内存结构解析》文章吧,也可关注golang学习网公众号了解相关技术文章。

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>