slots 定义后还能动态添加属性吗?会发生什么

定义__slots__后直接赋值未声明属性会报AttributeError;若需动态添加,须显式包含'__dict__';继承时父类无__slots__则子类定义无效;其核心是优化内存与性能,非强制封禁属性。

定义 __slots__ 后直接赋值新属性会报错

不能。一旦类中定义了 __slots__,Python 就会禁用默认的 __dict__,实例只能拥有 __slots__ 中声明的那些属性。尝试给未声明的属性赋值,会触发 AttributeError

class Person:
    __slots__ = ['name', 'age']

p = Person() p.name = 'Alice' # ✅ 允许 p.age = 30 # ✅ 允许 p.email = 'a@b.c' # ❌ AttributeError: 'Person' object has no attribute 'email'

想临时加属性?得显式提供 __dict__

如果确实需要动态添加属性(比如调试、临时打补丁),必须在 __slots__ 中**显式包含 '__dict__'**。这样 Python 会为实例保留 __dict__,允许动态设置任意属性:

  • __slots__ = ['name', 'age', '__dict__'] → 动态属性可用,但内存开销略增
  • 不加 '__dict__' 就彻底禁止,连 setattr(p, 'x', 1) 都失败
  • 注意:__dict__ 只影响实例,不影响类本身的属性查找逻辑

__slots__ 和继承一起用时更易踩坑

子类若也定义 __slots__,它只会限制子类新增的属性;父类的 __slots__ 仍生效。但如果父类没定义 __slots__,子类加了也没用——因为父类实例已有 __dict__,子类实例自动继承它:

  • 类有 __slots__,子类没写 → 子类实例仍受父类 __slots__ 限制
  • 父类没 __slots__,子类写了 → 实际无效,子类实例仍可动态加属性
  • 双方都定义,且子类想扩展 → 必须确保父类没 __dict__,否则约束被绕过

性能与设计意图比“能不能加”更重要

__slots__ 的核心目的不是“封死属性”,而是节省内存和加速属性访问。如果你频繁需要动态加属性,说明这个类可能不适合用 __slots__ —— 它更适合结构稳定、实例量大的场景(比如 ORM 模型、数据容器)。强行加 '__dict__' 会让优化失效,还可能掩盖设计问题。

真正容易被忽略的是:__slots__ 不影响类属性、也不阻止通过 __setattr__ 拦截并自定义逻辑,但那属于另一层控制,和“能否直接赋值”是两回事。