__slots__ 能省内存是因为禁用实例的 __dict__ 字典,改用固定偏移的 C 结构体存储属性,避免哈希表开销;单实例省数百字节,万级实例可省几 MB 到几十 MB。

为什么 __slots__ 能省内存?先看它干了什么
Python 默认为每个实例动态创建一个 dict 字典来存属性,哪怕只存一个字段,也要扛住整个字典的开销(哈希表结构、预留空槽、键字符串对象等)。而 slots 会禁用 dict,改用固定偏移的 C 结构体式存储——类似 C 的 struct,没有哈希查找、没有动态扩容、不存键名,纯靠预定义字段名查内存偏移。
这带来的实际效果是:单个实例能省下几百字节。万级实例时,内存差可能达几 MB 到几十 MB。
怎么正确定义 __slots__?常见写法和陷阱
-
slots 必须是类变量,类型是 tuple、list 或 set(推荐用 tuple,不可变更明确)
- 字段名必须是字符串,且不能包含空格或特殊符号
- 如果父类用了
slots,子类也想用,必须显式定义自己的 slots;否则子类会自动获得 dict,前功尽弃
- 不要试图在
slots 里放方法名、类属性名或以双下划线开头的“私有”字段(如 "__name"),它们不会被限制,也不起作用
- 若需支持动态属性(极少数场景),可显式把
"dict" 加进 slots,但这就失去了主要意义
slots 必须是类变量,类型是 tuple、list 或 set(推荐用 tuple,不可变更明确)slots,子类也想用,必须显式定义自己的 slots;否则子类会自动获得 dict,前功尽弃slots 里放方法名、类属性名或以双下划线开头的“私有”字段(如 "__name"),它们不会被限制,也不起作用"dict" 加进 slots,但这就失去了主要意义示例正确写法:
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
加了 __slots__ 后哪些操作会报错?提前避坑
- 尝试给实例赋值未声明的属性 → 触发
AttributeError: 'Point' object has no attribute 'z'
- 访问
obj.dict → 报 AttributeError(除非你手动加了 "dict" 进 slots)
- 使用
vars(obj) 或 dir(obj) 查属性时,不会显示 slots 字段以外的内容(dir 可能仍列一些继承来的属性,但不可赋值)
- 用
dataclasses.dataclass 时,别直接套 slots = True —— 要写成 @dataclass(slots=True)(Python 3.10+),否则无效
性能与兼容性要注意什么?不是所有地方都适合上
-
slots 类无法被 pickle(除非自定义 getstate / setstate),因为默认序列化依赖 dict
- 不能被 weakref 引用,除非额外加入
"weakref" 到 slots 元组里
- 与某些 ORM(如 SQLAlchemy 旧版)或调试工具(如
pdb 动态注入)存在兼容问题,因为它们依赖 dict 注入临时状态
- 继承链稍复杂时(比如多层 mixin + slots),容易漏掉某一级的 slots 声明,导致底层突然恢复
dict,内存省得不彻底又不易察觉
AttributeError: 'Point' object has no attribute 'z'
obj.dict → 报 AttributeError(除非你手动加了 "dict" 进 slots)vars(obj) 或 dir(obj) 查属性时,不会显示 slots 字段以外的内容(dir 可能仍列一些继承来的属性,但不可赋值)dataclasses.dataclass 时,别直接套 slots = True —— 要写成 @dataclass(slots=True)(Python 3.10+),否则无效-
slots类无法被 pickle(除非自定义getstate/setstate),因为默认序列化依赖dict - 不能被 weakref 引用,除非额外加入
"weakref"到slots元组里 - 与某些 ORM(如 SQLAlchemy 旧版)或调试工具(如
pdb动态注入)存在兼容问题,因为它们依赖dict注入临时状态 - 继承链稍复杂时(比如多层 mixin + slots),容易漏掉某一级的 slots 声明,导致底层突然恢复
dict,内存省得不彻底又不易察觉
真正需要它的地方很具体:高频创建/销毁的轻量数据载体(如解析日志行、网络包字段、几何点、事件消息体),且确认不需动态增删属性、不走 pickle、不用弱引用。其他情况,先 profile 再决定。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











