__slots__能省内存,因为其禁用实例__dict__字典(约240字节开销),改用固定偏移的c结构体布局,仅保留声明字段所需空间;但需确保类不继承带__dict__的父类且放弃动态属性能力。

为什么 __slots__ 能省内存?
Python 默认为每个实例动态分配一个 __dict__ 来存储属性,哪怕只存一两个字段,这个字典本身就要占用几百字节。而 __slots__ 禁用 __dict__,改用固定偏移的 C 结构体存储属性,直接跳过哈希表开销。
实测:一个空类实例约 56 字节;加了 __dict__ 后涨到 200+ 字节;启用 __slots__ = ('x', 'y') 后回落到约 80 字节——不是“稍微省一点”,是数量级差异。
怎么写 __slots__ 才不报错?
常见错误是继承时没处理好父类和子类的 __slots__ 关系,导致 AttributeError 或意外保留 __dict__。
- 父类定义了
__slots__,子类**必须显式定义**自己的__slots__,否则会继承父类限制但无法新增属性 - 子类想保留动态属性(即允许
obj.new_attr = 1),得在__slots__里显式加入'__dict__'—— 但这就抵消了大部分内存优势 - 如果父类没定义
__slots__,子类加了也没用:父类实例仍带__dict__,子类实例也会继承它
正确写法示例:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
<p>class ColoredPoint(Point):
<strong>slots</strong> = ('color',) # 不写 <strong>dict</strong>,就不能动态设新属性
def <strong>init</strong>(self, x, y, color):
super().<strong>init</strong>(x, y)
self.color = color</p>
__slots__ 会影响哪些操作?
它不是纯优化开关,会真实改变类行为:
- 不能对实例赋值未声明的属性,比如
p.z = 3会触发AttributeError -
vars(p)和dir(p)行为受限:vars()直接抛TypeError(因无__dict__) - 不能被
pickle默认序列化(除非自定义__getstate__) - 装饰器如
@dataclass与__slots__共存需显式传参:@dataclass(slots=True)(Python 3.10+)
如果你依赖 getattr(obj, name, default) 动态读属性,只要属性名在 __slots__ 里就完全正常;但 setattr(obj, 'missing', val) 就会失败。
什么情况下不该用 __slots__?
它适合大量轻量实例(如几何点、解析树节点、ORM 模型行),但不适合需要灵活扩展的类:
- 类要被用户 monkey patch(比如测试中临时加方法),
__slots__会拦住obj.__dict__['new_method'] = lambda: ... - 用到了
__weakref__却没在__slots__里声明,会导致弱引用失效(需加'__weakref__') - 类本身极少实例化(比如全局配置类),省下的内存微不足道,反而增加维护成本
最易忽略的一点:一旦发布为公共 API,加了 __slots__ 就锁死了属性名集合,后续增删字段都算破坏性变更。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











