__slots__ 能省内存是因为禁用实例的 __dict__,属性直接映射到预分配的固定内存偏移,减少约240字节/实例开销;正确声明需为类变量、tuple/list/set类型,并在继承链中显式定义。

为什么 __slots__ 能省内存?本质是干掉了 __dict__
Python 默认为每个实例动态分配一个 __dict__ 字典来存储属性,哪怕只存 1 个字段,也要额外占用 ~240 字节(64 位 CPython)。启用 __slots__ 后,实例不再有 __dict__,属性直接映射到预分配的固定内存偏移上,对象头 + 数据区更紧凑。实测 10 万个简单对象,用 __slots__ 可减少 50%~70% 内存占用。
怎么正确声明 __slots__?必须是类变量且值为序列
声明错误会导致完全失效,比如写成实例变量、字符串(非元组/列表)、或漏掉父类 slots —— 这些都会让子类悄悄回退到带 __dict__ 的模式。
-
__slots__必须定义在类体顶层,类型是tuple、list或set(推荐tuple) - 若父类已定义
__slots__,子类也需显式定义,否则继承链中断,子类自动获得__dict__ - 想允许动态属性?加
'__dict__'到__slots__里,但这就抵消了大部分内存收益
class Point:
__slots__ = ('x', 'y') # ✅ 正确:元组,明确字段
<p>class ColorPoint(Point):
<strong>slots</strong> = ('color',) # ✅ 正确:延续 slots 链</p><h1>若这里不写 <strong>slots</strong>,ColorPoint 实例就会有 <strong>dict</strong></h1><p>用了 __slots__ 后哪些操作会报错?提前踩坑
禁用动态属性不是 bug,是设计结果。常见报错包括:
立即学习“Python免费学习笔记(深入)”;
- 给未声明字段赋值 →
AttributeError: 'Point' object has no attribute 'z' - 访问
obj.__dict__→AttributeError: 'Point' object has no attribute '__dict__' - 用
setattr(obj, 'x', 1)动态设值 → 同样报 AttributeError(除非字段名在__slots__中) - 类本身仍可有
__dict__(存方法、类属性),但实例没有
调试时别依赖 vars(obj),改用 [(s, getattr(obj, s)) for s in obj.__slots__] 查属性值。
什么场景下不该用 __slots__?别为了省内存牺牲灵活性
它不是银弹。以下情况建议放弃:
- 需要运行时动态增删属性(如 ORM 映射、配置对象、mock 对象)
- 类被 pickle 序列化且依赖
__dict__(虽可配合__getstate__补救,但增加复杂度) - 使用某些框架(如旧版 Django 模型、attrs 不带
slots=True)可能冲突 - 对象数量少于几千个,内存节省微乎其微,反而增加维护成本
真正值得上的典型场景:高频创建的轻量数据载体,比如解析 CSV 行、网络包结构体、游戏中的粒子状态 —— 这些地方每省 100 字节,10 万实例就省下 10MB。


















