__slots__能减少内存占用,因其禁用实例的__dict__字典(节省约240字节/实例),改用连续内存槽位存储属性,类似C结构体,使内存更紧凑、访问更快,通常节省40%–60%内存。

为什么 __slots__ 能减少内存占用
Python 默认为每个实例动态创建一个 __dict__ 字典来存储属性,哪怕只存一两个字段,也要额外承担字典的哈希表开销(通常至少 240 字节)。启用 __slots__ 后,实例不再生成 __dict__,属性被固定分配在连续内存块中,类似 C 结构体——对象大小直接由字段数量和类型决定,通常能节省 40%–60% 内存。
但注意:这个优化只对「大量短生命周期实例」有效(比如模型类、数据行、树节点),单个对象几乎看不出差别;而且一旦用了 __slots__,就不能动态添加新属性,也不能继承自未定义 __slots__ 的父类(除非显式处理)。
怎么正确声明 __slots__
在类定义中直接赋值一个字符串元组:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
常见错误和要点:
- 必须是
tuple、list或其他可迭代对象,但推荐用tuple(不可变更安全) - 如果父类有
__slots__,子类也必须声明,否则父类的__slots__不生效 - 想保留
__dict__(例如调试或临时扩展),就把'__dict__'加进__slots__元组里——但这会基本抵消内存收益 - 不能只写字符串(如
__slots__ = 'x'),会被当成单字符迭代,等价于('x',)—— 看似侥幸,实则危险
__slots__ 和常见操作的兼容性问题
启用后,部分动态行为会失效,容易在运行时突然报错:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 尝试赋值未在
__slots__中声明的属性 →AttributeError: 'Point' object has no attribute 'z' - 使用
vars(obj)或访问obj.__dict__→AttributeError(除非显式包含'__dict__') - 用
copy.deepcopy()深拷贝含__slots__的对象可能失败,需配合自定义__getstate__/__setstate__ - 序列化(如
pickle)默认支持,但若类还定义了__getstate__,需手动处理__slots__中的字段
不是所有场景都适合:比如 ORM 模型、需要灵活挂载方法或配置的类,硬加 __slots__ 反而增加维护成本。
验证内存变化的实际方法
别靠猜测,用 sys.getsizeof() + __slots__ 对比是最直接的方式:
import sys
<p>class RegularPoint:
def <strong>init</strong>(self, x, y):
self.x = x
self.y = y</p><p>class SlottedPoint:
<strong>slots</strong> = ('x', 'y')
def <strong>init</strong>(self, x, y):
self.x = x
self.y = y</p><p>p1 = RegularPoint(1, 2)
p2 = SlottedPoint(1, 2)
print(sys.getsizeof(p1)) # 通常 56 或更大
print(sys.getsizeof(p2)) # 通常 32 或更小
</p>
注意:sys.getsizeof() 不计算引用对象(如 self.x 是 int,不计入),仅反映实例自身结构开销。真要压测大规模对象内存,建议用 memory_profiler 或 tracemalloc。
真正关键的是:你得确认这些实例确实密集存在,且生命周期可控;否则花时间加 __slots__ 却没换来实际收益,或者因忽略继承链/序列化逻辑引入隐性 bug,就得不偿失了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










