__slots__能显著减少单个实例内存占用,因禁用__dict__字典而改用紧凑结构体存储,内存可降为1/3~1/2,但仅在属性固定且实例量大时适用;滥用会导致动态赋值失败、序列化异常及维护成本陡增。

直接说结论:用 __slots__ 能显著减少单个实例的内存占用,但只在类有大量实例且属性固定时才值得加;加错或滥用反而会让代码更难维护、甚至出错。
为什么 __slots__ 能省内存?
Python 默认为每个实例创建一个 __dict__ 字典来存属性,哪怕只存 2–3 个字段,这个字典本身就要占几百字节。而 __slots__ 告诉解释器:“这个类只允许这几个属性”,于是实例不再生成 __dict__,改用紧凑的结构体存储,内存可降为原来的 1/3~1/2。
但前提是:你确定所有实例都只用到声明的那些属性,且不依赖动态赋值、setattr、getattr 或 pickle 兼容性。
怎么正确声明 __slots__?
在类定义中直接赋值一个字符串元组,字段名必须和实际使用的属性名完全一致(包括下划线):
class Point:
__slots__ = ('x', 'y', '_label')
<pre class="brush:python;toolbar:false;">def __init__(self, x, y, label=''):
self.x = x
self.y = y
self._label = label
常见错误包括:
- 漏写继承父类的
__slots__:子类不会自动合并父类的__slots__,必须显式包含或重复声明 - 写了
__slots__还手动定义__dict__:会触发TypeError: __dict__ is not allowed with __slots__ - 把方法名、类变量或 property 名误放进
__slots__:只放**实例属性名**,方法和@property不需要、也不应该放
哪些场景下不该用 __slots__?
它不是银弹,以下情况加了反而坏事:
- 类需要动态添加属性(比如用
obj.new_field = 42)→ 直接报AttributeError - 用了
__dict__做调试、序列化或反射(如vars(obj))→ 实例没__dict__,会失败 - 类被 pickle 序列化 → 默认协议不支持
__slots__类,需自定义__getstate__和__setstate__ - 类要被
dataclasses或pydantic等库装饰 → 多数现代库默认不兼容__slots__,得查文档确认是否支持
验证内存节省效果的实际方法
别猜,用 sys.getsizeof() 对比,但注意:它只返回对象本身开销,不含引用对象(如字符串、列表)。更准的做法是用 pympler:
from pympler import asizeof p = Point(1.0, 2.0) print(asizeof.asizeof(p)) # 包含所有嵌套引用的真实大小
测试时务必用成百上千个实例对比——单个实例省几十字节,百万实例就能差几十 MB。如果实例数很少,或者属性本身是大对象(比如存了一个 numpy.ndarray),__slots__ 的收益几乎可以忽略。
最常被忽略的一点:一旦加了 __slots__,就锁死了该类的扩展能力。后续加新属性、换框架、做 monkey patch,都得回头改 __slots__ 元组——这个维护成本,比省下的那点内存重得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











