__slots__能减少内存占用,因禁用实例__dict__而改用固定结构存储属性,内存可降40%–60%;需显式定义为字符串元组,子类及多继承时须显式合并父类slot字段。

为什么 __slots__ 能减少内存占用
Python 默认为每个实例创建一个 __dict__ 字典来存储属性,哪怕只存一两个字段,这个字典本身就要占用几百字节。启用 __slots__ 后,实例不再生成 __dict__,而是用固定大小的结构(类似 C struct)存放预定义的属性,内存可减少 40%–60%,尤其在大量小对象场景下效果明显。
注意:__slots__ 不是万能压缩工具——它只对实例属性生效,不影响类属性、方法或继承链上的其他开销。
如何正确定义 __slots__ 并避免常见报错
必须在类定义中显式声明为元组或列表(推荐元组),且仅包含字符串形式的属性名:
class Point:
__slots__ = ('x', 'y') # ✅ 正确:元组,无逗号错误,无空格
def __init__(self, x, y):
self.x = x
self.y = y
容易踩的坑:
- 写成
__slots__ = 'x', 'y'(缺少括号 → 实际是字符串拼接,等价于'xy') - 包含空格或非法标识符,如
'x coord'或'x-y'→ 实例赋值时报AttributeError - 子类未声明
__slots__,但父类有 → 子类实例仍会生成__dict__,完全失效 - 同时定义
__dict__和__slots__→ Python 直接抛TypeError: __dict__ assignment not allowed
动态添加属性被禁用时该怎么应对
启用 __slots__ 后,任何未列在 __slots__ 中的属性赋值都会触发 AttributeError: 'Point' object has no attribute 'z'。这不是 bug,是设计使然。
如果你确实需要临时扩展字段(比如调试、打补丁),有且仅有两种合法方式:
- 在
__slots__中预先加入预留字段,例如__slots__ = ('x', 'y', '_debug_info'),然后用它存临时数据 - 彻底放弃
__slots__,改用普通类 + 显式校验(比如在__setattr__里拦截非法 key)
别试图通过 object.__setattr__(obj, 'z', 123) 绕过限制——只要 __slots__ 存在且没包含 'z',照样报错。
继承与多父类下 __slots__ 的行为边界
多个父类都定义了 __slots__ 时,Python 不会自动合并;子类必须显式声明自己的 __slots__,否则继承关系中只要有一个父类有 __slots__ 而子类没有,子类就仍会有 __dict__,导致内存优化失效。
更隐蔽的问题:
- 如果父类 A 有
__slots__ = ('a',),父类 B 有__slots__ = ('b',),而子类 C 同时继承 A 和 B,却只写__slots__ = ('c',)→ 那么 A 的a和 B 的b无法被 C 实例访问(会报 AttributeError) - 解决办法:C 必须显式列出全部需要的字段,即
__slots__ = ('a', 'b', 'c'),且确保所有父类的 slot 字段都在其中 - 若某个父类没定义
__slots__(比如内置类型list或第三方类),则子类无法对其添加 slot 限制,该路径上仍存在__dict__
实际项目中,一旦引入 __slots__,就得把它当作接口契约的一部分:所有子类、所有协作模块都必须知情并配合,否则很容易在深层继承或 mixin 组合时突然崩掉。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











