@dataclass加__slots__能提速,是因为禁用__dict__后属性访问从字典哈希查找变为内存偏移直取,实测读取快20%~35%、内存降约40%;但需用@dataclass(slots=True)显式启用,且父类也须支持,否则失效。

为什么 @dataclass 加 __slots__ 能提速?
因为默认的 @dataclass 不启用 __slots__,实例仍带 __dict__,属性访问走动态查找;加上后强制只允许预定义字段,跳过字典哈希查找,直接通过偏移量读内存——实测属性读取快 20%~35%,内存占用降约 40%(尤其小对象密集场景)。
但注意:一旦启用 __slots__,就不能再动态赋值新属性,也不能用 setattr() 写未声明字段,否则抛 AttributeError: 'X' object has no attribute 'y'。
怎么正确开启 slots(不是加 slots=True 就完事)
必须显式传参 slots=True,且不能和 __slots__ 手动定义共存;同时要确保所有父类也用了 slots=True,否则子类 slots 会被父类的 __dict__ “污染”失效。
-
@dataclass(slots=True)是唯一有效方式;@dataclass+ 手动写__slots__ = ('a', 'b')会报错或静默失效 - 如果继承自另一个 dataclass,父类也得带
slots=True,否则子类 slots 不起作用 - 若父类是非 dataclass 的普通类,且定义了
__slots__,子类仍需设slots=True,Python 3.10+ 会自动合并 slots 元组
示例:
立即学习“Python免费学习笔记(深入)”;
@dataclass(slots=True)
class Point:
x: float
y: float
<p>p = Point(1.0, 2.0)</p><h1>p.z = 3.0 # AttributeError!哪些情况不能用 slots=True?
不是所有 dataclass 都适合。以下任一条件成立,就别硬上 slots:
- 需要运行时动态增删属性(比如用
__dict__.update()或setattr(obj, name, val)) - 用了
__post_init__里给实例挂非字段属性(如缓存、上下文句柄),除非你把它也写进字段并设init=False - 类被 pickle 序列化且依赖
__dict__(Python 3.10 默认 pickle 支持 slots,但某些旧库或自定义协议可能不兼容) - 用到了
weakref.ref()指向该实例——slots 类默认不支持弱引用,除非额外加__weakref__到 slots 中(即字段里显式声明__weakref__: Any = field(repr=False, init=False, default=None))
性能差异真的明显吗?关键看你怎么测
单纯 obj.field 访问,slots 快;但如果你在循环里反复调用带验证逻辑的 property、或大量触发 __getattribute__,那 slots 带来的收益会被吞掉。真实瓶颈往往不在这里。
- 用
timeit测纯属性访问时,建议跑 100 万次以上,避免噪声干扰 - 检查是否真生效:打印
Point.__slots__应为('x', 'y');hasattr(Point(), '__dict__')应返回False - 内存对比用
sys.getsizeof()不准(它不计对象内部引用),推荐用pympler.asizeof.asizeof()
真正容易被忽略的是:slots 会让类失去对 monkey patch 的兼容性,调试时临时加方法会失败——这在测试或 REPL 探索阶段很碍事。


















