__slots__ 能大幅节省内存,因其禁用 __dict__ 字典而改用固定偏移数组存储属性,单个对象可从超400字节降至40–60字节,百万实例节省230MB+;需正确定义为元组、所有继承类均须声明、显式添加__weakref__等,否则失效或报错。

__slots__ 为什么能省下大量内存
Python 默认给每个实例配一个 __dict__ 字典来存属性,哪怕只设两个整数字段,这个字典本身就要占约 240 字节(64 位环境),外加键字符串、值对象、哈希表预留空间。创建百万个 User 或 Point 时,光是这些字典就吃掉上百 MB 内存。
__slots__ 的核心动作是:禁用 __dict__,改用固定偏移量数组存储声明过的属性——类似 C 结构体。每个属性只占一个指针大小(通常 8 字节),没有哈希查找开销,也没有动态扩容成本。
- 实测:单个普通对象占 56+ 字节,加
__dict__后实际常超 400 字节;启用__slots__后可压到 40–60 字节 - 10 万个对象节省约 23MB+;100 万个就是 230MB+;某些场景实测压缩率达 70%
- 不是“略微优化”,而是百万级对象下的刚性需求——否则 RSS 内存暴涨、GC 频繁触发、CPU 花在清理小字典上
怎么正确定义 __slots__ 才生效
写错格式或漏掉关键点,__slots__ 就白加了。它只对新创建的实例起作用,且必须是类变量、不可变结构。
- 必须用元组:
__slots__ = ('x', 'y', 'id')—— 列表虽能运行但不安全,字符串如'x'会被当迭代器拆成('x',),易出错 - 空元组表示完全禁止动态属性:
__slots__ = () - 需要弱引用(比如缓存、观察者模式)?必须显式加:
__slots__ = ('x', 'y', '__weakref__'),否则weakref.ref(obj)直接失败 - 别在已有类上“补”
__slots__:已创建的实例不受影响;只对后续 new 出来的实例生效
继承链里 __slots__ 为什么突然失效
子类没定义 __slots__,父类的声明就作废——子类实例仍会生成 __dict__,整条链“破防”。这不是 bug,是 Python 的明确行为。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
立即学习“Python免费学习笔记(深入)”;
- 规则很硬:继承链上**所有想省内存的类**,都必须各自定义
__slots__ - 子类
__slots__不会自动合并父类字段;要手动包含,或留空(但__slots__ = ()≠ 继承父类 slots) - 多重继承时,若多个父类
__slots__有同名字段,Python 直接抛TypeError - 只要任一父类没设
__slots__,所有子类实例回退字典存储——哪怕你子类写了,也无效
用了 __slots__ 后哪些事不能干了
它不是封装工具,而是内存优化机制,代价是牺牲一部分灵活性。很多看似合理操作会在运行时报错,而且错误信息不友好。
- 不能临时加调试属性:
obj.debug_flag = True→AttributeError: 'Point' object has no attribute 'debug_flag' - 不能用
vars(obj)或obj.__dict__.update(...);序列化需绕过:dataclasses.asdict()不兼容,得手写__getstate__ - 依赖
__dict__的库会出问题:Django ORM 模型、未设__slots__ = True的@dataclass、某些 pickle 工具(如 jsonpickle) - 别把它用在预期被继承并扩展属性的基类上——除非你能确保所有子类都严格定义自己的
__slots__
真正容易被忽略的是继承链的完整性:哪怕只漏一个中间类没加 __slots__,前面所有优化都归零。上线前用 sys.getsizeof() 对比几个实例,再检查 hasattr(obj, '__dict__'),比靠文档更可靠。

















