__slots__最直接有效的方式是定义为类变量元组,禁用__dict__并仅允许声明属性赋值;常见错误是未声明属性导致AttributeError,继承时子类须显式定义才能生效。

用 __slots__ 锁定实例属性集
最直接有效的方式是定义 __slots__。它会禁用默认的 __dict__,只允许在 __slots__ 元组中声明的属性名被赋值。
常见错误现象:没加 __slots__ 时,即使类里没定义 name,obj.name = "xxx" 也能成功,后续难以追踪非法字段。
实操建议:
-
__slots__必须定义为类变量,类型是tuple、list或str(单个字符串);写成list更易维护,但最终会被转为tuple - 如果继承自父类,且父类用了
__slots__,子类也必须定义,否则父类的__slots__不生效 - 一旦启用
__slots__,就不能再通过obj.__dict__查看属性——这是设计使然,不是 bug
示例:
立即学习“Python免费学习笔记(深入)”;
class Person:
__slots__ = ("name", "age")
<p>p = Person()
p.name = "Alice" # ✅ 允许
p.age = 30 # ✅ 允许
p.score = 95 # ❌ AttributeError: 'Person' object has no attribute 'score'
为什么不用 __setattr__ 拦截?
有人想重写 __setattr__ 做白名单校验,但这不推荐——它无法阻止通过 object.__setattr__ 绕过,且性能开销大、逻辑易漏。
使用场景有限:仅适合需要运行时动态控制(比如调试模式临时开放)、或配合元类做复杂策略的极少数情况。
关键差异:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
-
__slots__是编译期/实例化期的硬约束,C 层级拦截,不可绕过 -
__setattr__是纯 Python 方法,任何地方调用object.__setattr__(obj, "x", v)都能跳过校验 - 若类中定义了
__slots__,Python 会自动忽略自定义的__setattr__(除非显式调用)
兼容性与副作用要注意什么
__slots__ 不是银弹,用错会导致意外行为。
常见坑点:
- 不能给对象加
__dict__或__weakref__,除非显式把它们加入__slots__(如__slots__ = ("name", "__weakref__")) - 用
pickle序列化时,若没处理好__slots__,可能报AttributeError: __dict__ - IDE 和静态检查工具(如 mypy)通常能识别
__slots__并提示非法赋值,但运行时仍需靠它兜底 - 装饰器或数据类(
@dataclass)默认不启用__slots__,需手动传slots=True
例如 dataclass 写法:
@dataclass(slots=True)
class Config:
host: str
port: int
动态加属性的需求真的该拦吗
有时候“防止加属性”背后其实是设计模糊:到底是防误操作,还是防恶意篡改?
如果是前者,__slots__ 足够;如果是后者,Python 本身不提供运行时安全模型——哪怕锁死属性,对方仍可替换方法、打 monkey patch、甚至修改字节码。
真正该关注的是:
- 是否所有属性都应在初始化时确定?如果不是,考虑用
__post_init__或工厂函数封装构造逻辑 - 是否需要用属性描述符(
@property+__set__)控制赋值逻辑,而非单纯禁止 - 是否该用不可变对象(
frozen=Truein dataclass)替代“只禁加属性”
硬拦属性不如厘清边界——Python 的哲学不是锁死,而是明确契约。

















