用 slots 能有效禁止实例动态添加未声明的属性并降低内存占用,但需显式列出所有允许属性名,且禁用 __dict__;其原理是移除实例的属性字典而改用固定结构体存储。
直接说结论:用 __slots__ 能有效禁止实例动态添加未声明的属性,同时降低内存占用;但它不是“开关式”防护,一旦启用就必须显式列出所有允许的属性名,且会禁用 __dict__。
为什么 __slots__ 会阻止动态添加属性
Python 默认每个实例都有一个 __dict__ 字典来存储属性,所以 obj.new_attr = 123 实际是往这个字典里塞键值对。而定义了 __slots__ 后,解释器不再为实例分配 __dict__,改用固定大小的结构体存储预设字段——没有字典,自然无法写入未声明的键。
常见错误现象:AttributeError: 'MyClass' object has no attribute 'xxx' 就是在尝试给 __slots__ 未包含的属性赋值时触发的。
- 类中定义
__slots__ = ('name', 'age')→ 实例只能有name和age两个实例属性 - 若还写了
__slots__ = ('name', 'age', '__dict__'),则仍允许动态添加(但失去内存优势) - 类属性、方法、
__weakref__不受__slots__限制,该禁的只是实例层面的新增字段
__slots__ 对继承和子类的影响
父类用了 __slots__,子类默认不会自动继承该限制;但如果子类也定义了 __slots__,它只约束自己新增的属性,不覆盖父类已声明的字段。
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
- 父类
class A: __slots__ = ('x',),子类class B(A): __slots__ = ('y',)→B实例可存x和y,但不能加z - 若子类没定义
__slots__,它会获得完整的__dict__,从而绕过父类的所有限制 - 想彻底锁死,所有参与继承链的类都得显式声明
__slots__
容易被忽略的兼容性陷阱
__slots__ 看似简单,但几个细节常导致意外行为:
- 不能和
__getstate__/__setstate__混用,除非手动处理__slots__字段序列化 - 使用
dataclasses时,@dataclass(slots=True)是等效替代,但老版本 Python 不支持 - 调试时习惯用
vars(obj)查属性?它依赖__dict__,__slots__下会报错,得改用obj.__slots__配合getattr() - 动态绑定方法(如
types.MethodType)不受影响,因为方法绑定到实例上的是可调用对象,不是“属性名=值”这种数据字段
真正难处理的不是加属性本身,而是团队协作中有人忘了查基类有没有 __slots__,结果在子类里随意加字段,运行时才暴露问题——这类错误不会在静态检查阶段浮现。