__slots__ 能省内存是因为禁用 __dict__ 后避免每个实例的哈希表开销,改用固定偏移访问字段;但调试器因依赖 __dict__ 而失效,如 pdb 打印 self 报错或显示为空,vars(obj) 抛 typeerror。

为什么 __slots__ 能省内存,但会让你的调试器变“瞎”
因为 __slots__ 禁用了默认的 __dict__,对象不再动态存属性,而是用固定偏移量访问字段——这省了每个实例的哈希表开销,但所有依赖 __dict__ 的工具(比如 pdb、pprint、IDE 变量面板)都看不到未声明的属性,甚至可能报 AttributeError。
- 典型现象:
pdb里打印self显示空字典或报错,vars(obj)报TypeError: object has no __dict__ - 只对实例生效:类本身、父类、子类都不自动继承
__slots__,子类若没定义会重新启用__dict__ - 想保留调试能力?可以显式加
'__dict__'到__slots__里,但那就几乎不省内存了 - 注意兼容性:
__slots__类不能被weakref引用,除非也加上'__weakref__'
__slots__ 和继承一起用时,哪些写法会直接报错
Python 不允许子类和父类同时定义 __slots__ 且子类没声明任何新字段时隐式启用 __dict__,但更常见的是因拼写或继承顺序导致的冲突。
- 父类有
__slots__ = ('x', 'y'),子类写__slots__ = ()—— 合法,但子类不能新增属性 - 父类有
__slots__,子类没定义__slots__—— 子类自动获得__dict__,内存优势全丢 - 父类没
__slots__,子类定义了 —— 允许,但子类实例仍带__dict__(因为父类已生成) - 两个父类都有
__slots__,且字段名重复?Python 3.10+ 会报TypeError: multiple bases have instance lay-out conflict
什么时候加 __slots__ 真的值得,什么时候纯属自找麻烦
它不是性能银弹,只有在创建海量轻量对象(比如解析 CSV 行、树节点、ORM 模型实例)时才体现价值;日常业务类、配置类、测试 mock 类基本不用。
- 实测参考:10 万个实例,每个 3 个字段,用
__slots__可比普通类少占 30%~50% 内存(取决于字段数和 Python 版本) - 别用在需要动态赋值的地方:比如
obj.new_field = 42会直接失败,除非你明确写了'__dict__' - 别用在要被
dataclasses或pydantic包裹的类上——它们内部机制和__slots__冲突,容易出不可预测行为 - 序列化/反序列化(如
json.dumps)会失败,因为默认不认__slots__字段,得手动实现__getstate__或用asdict()类工具
如何快速验证 __slots__ 是否生效、有没有意外泄漏 __dict__
别靠感觉,用 sys.getsizeof() 和 hasattr() 组合检查最可靠。注意:getsizeof 不递归,只看对象头和直接字段。
立即学习“Python免费学习笔记(深入)”;
- 检查是否禁用
__dict__:hasattr(obj, '__dict__')应该返回False - 对比内存:分别实例化 1000 个对象,用
sum(sys.getsizeof(o) for o in instances)看差值 - 字段写入测试:
obj.x = 1成功,obj.y = 2(y 不在__slots__中)应抛AttributeError - 注意陷阱:
__slots__是类变量,必须定义在类体顶层,写在if块里或函数中会被忽略,还不报错
真正难的不是加 __slots__,是判断哪些字段该进槽、哪些该放 __dict__、以及团队里其他人改代码时会不会无意绕过它——一旦漏掉一个 __slots__ 声明,整个优化就失效了。










