__slots__ 能限制属性绑定并省内存,因其禁用 __dict__ 改用紧凑 c 结构体存储;仅约束实例属性,不支持动态新增未声明属性,适用于属性固定且数量庞大的轻量对象。

为什么 __slots__ 能限制属性绑定又省内存
因为 Python 默认用 __dict__ 存实例属性,是个哈希表,灵活但占内存、查得慢;__slots__ 会让解释器改用固定偏移的 C 结构体存储,不生成 __dict__,自然无法动态绑新属性,也省了哈希表开销。
典型场景:高频创建大量轻量对象(如 ORM 模型实例、解析后的日志条目),单个对象省几十字节,百万级就差几十 MB。
- 没
__slots__的类,每个实例带一个__dict__(约 240 字节起步)和一个__weakref__ - 加了
__slots__ = ('x', 'y'),实例变成紧凑结构,大小接近struct { PyObject *; int x; int y; } - 继承自内置类型(如
int、str)或有__dict__的父类时,__slots__无效或需显式处理
怎么写 __slots__ 才不报 AttributeError
错误常发生在:忘了把父类的 __slots__ 合并进来,或试图给没声明的属性赋值。
- 子类定义
__slots__时,父类也必须定义,否则子类仍会有__dict__,失去约束意义 - 若父类没
__slots__,子类加了也没用——Python 不会自动禁用__dict__ - 想保留动态属性能力?可以显式把
'__dict__'加进__slots__元组,但那就失去内存优势了 - 常见报错:
AttributeError: 'A' object has no attribute 'z',说明你写了self.z = 1,但'z'不在__slots__里
示例:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
<p>p = Point(1, 2)
p.z = 3 # AttributeError!
</p>
__slots__ 对方法、类属性和继承的影响
它只约束**实例属性**,不影响类属性、方法、描述符或 __new__/__init__ 行为。
- 类变量(如
count = 0)照常工作,不受__slots__影响 - property、@cached_property、描述符(
__get__)都能正常使用,它们本质是类属性 - 多重继承时,只要任意父类定义了
__slots__且不含'__dict__',所有子类都必须定义自己的__slots__,否则报TypeError: multiple bases have instance lay-out conflict - 想支持 pickle?得额外加
__getstate__和__setstate__,因为默认序列化依赖__dict__
什么情况下不该用 __slots__
不是所有类都适合——它牺牲灵活性换性能,用错反而添麻烦。
- 需要 monkey patch 动态加方法或属性的测试类、调试工具类,别加
- 用
__dict__做配置映射、通用数据容器(比如Config(**data))的类,加了就崩 - ORM 模型类(如 SQLAlchemy)通常已内部处理了
__slots__,自己乱加可能破坏 lazy loading 或事件钩子 - 不确定是否会被子类化,或子类需求不明时,先别加;加了等于强制所有下游代码遵守约束
真正该用的时候,往往是明确知道对象生命周期短、数量大、属性集固定——比如网络包解析器里的 IPHeader、游戏引擎里的 Vector3。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











