python对象内存布局由pyobject头(含引用计数和类型指针)与后续数据组成;实例属性存于__dict__字典,方法调用通过动态绑定实现;__slots__禁用__dict__并直接分配字段以节省内存和加速访问。

Python 中的类和对象在内存中并不是简单的数据容器,而是由一套精巧的对象模型支撑的动态结构。理解其内存布局,关键不在于“地址”或“字节”,而在于 类型指针、属性存储方式、方法绑定机制 三者的协同关系。
对象底层:PyObject + 实际数据
每个 Python 对象(如 a = 3、obj = MyClass())在 C 层都对应一个 PyObject 结构体,它包含两个核心字段:
- ob_refcnt:引用计数,用于垃圾回收
-
ob_type:指向该对象所属类型的指针(例如
int、dict或自定义类的PyTypeObject)
真正存放数据的部分(如整数的值、列表的元素指针数组、实例的属性字典)则紧跟在 PyObject 之后,由具体类型决定布局。比如 int 对象额外存一个 ob_digit 数组,而用户定义的实例默认只预留一个指向 __dict__ 的指针位置。
实例对象的属性去哪儿了?
普通实例(未使用 __slots__)的属性全部存在一个独立的字典对象中,即 obj.__dict__。这个字典本身也是一个 Python 对象,有自己的 PyObject 头和哈希表结构。
立即学习“Python免费学习笔记(深入)”;
Dbsite企业网站管理系统V1.5.0 秉承"大道至简 邦达天下"的设计理念,以灵巧、简单的架构模式构建本管理系统。可根据需求可配置多种类型数据库(当前压缩包支持Access).系统是对多年企业网站设计经验的总结。特别适合于中小型企业网站建设使用。压缩包内包含通用企业网站模板一套,可以用来了解系统标签和设计网站使用。QQ技术交流群:115197646 系统特点:1.数据与页
- 执行
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__。









