Python 动态属性绑定与 __slots__ 使用教程
在做数据量稍大的实体类(比如电商的商品、社交平台的用户信息)开发时,你有没有遇到过这些小麻烦?
- 同事手滑给临时测试的学生实例加了一堆
s.temp_xxx属性,忘了删除导致后续调试数据混乱? - 部署时发现几十万个小对象占用内存远超预期?
- 维护代码时不确定某个类“规定”了哪些核心属性,只能翻遍所有调用点?
这时候,Python 里的 __slots__ 就能帮你解决这些问题。不过在讲它之前,我们得先回忆下 Python 最“自由”的特性之一:运行时动态绑定属性和方法。
1. Python 动态绑定机制
Python 是纯动态语言,不像 Java/C++ 在编译时就锁定了类的属性和方法。在代码运行过程中,你可以随时给单个实例、甚至整个类追加属性或函数。
1.1 动态绑定单个实例的属性
这是最基础的操作,直接给实例赋值就行:
1.2 动态绑定单个实例的方法
属性可以单独加,函数也可以单独绑定给一个实例(但要注意用 types.MethodType 包装,把实例本身作为 self 传递):
1.3 动态绑定整个类的属性/方法
如果想让所有实例都能用新属性或新方法,直接给类赋值就行:
2. 用 __slots__ 给类“戴紧箍咒”
动态绑定虽然灵活,但“自由过头”就容易出问题。这时候 __slots__ 就登场了——它是一个元组或列表,定义了类的实例只能拥有的属性名称。
2.1 最基础的用法
直接在类内部声明 __slots__ = (允许的属性1, 允许的属性2, ...) 就行:
2.2 继承场景下的 __slots__
这里有两个常见的小坑,要特别注意:
- 默认不继承父类的
__slots__限制(除非子类也显式声明):如果父类用了__slots__,子类没写,那子类的实例会同时拥有父类的允许属性+自己的__dict__(可以随便加属性)。 - 如果子类也写了,允许的属性是「父类+子类」的并集:
2.3 现代 Python 开发的注意事项
别以为 __slots__ 只是限制属性——它还有几个核心隐藏特性:
2.3.1 节省内存!(这是它最重要的现代用途之一)
Python 默认给每个实例分配一个 __dict__ 字典存属性,字典本身是很占内存的(几十万个实例的话,可能差几十MB甚至GB)。用了 __slots__ 后,Python 会用更紧凑的固定大小结构存属性,不再创建 __dict__。
2.3.2 和 @property 完美兼容
有人担心:用了 __slots__ 是不是不能用属性装饰器了?完全不会!只要把 @property 对应的私有变量也加到 __slots__ 里就行:
2.3.3 别乱用!什么时候不适合用?
如果你的类需要经常动态扩展属性(比如做爬虫时临时抓字段的类),或者需要用 __dict__ 做属性操作(比如 vars(obj)、obj.__dict__.update(...)),就别加 __slots__。
3. 实际应用场景建议
根据 __slots__ 的特性,它最适合这几种情况:
- 大量创建的实体类:比如日志解析的单条记录、推荐系统的候选商品,能省很多内存。
- 框架/库的内部类:防止库的用户手滑给你的内部类加没用的属性,保证代码稳定性。
- 需要严格控制属性的业务类:比如金融系统的账户类,只允许定义好的属性(比如 id、balance、create_time),防止数据意外被篡改或污染。
4. 快速对比总结
最后总结一下:__slots__ 不是 Python 开发的“必需品”,但在合适的场景下用它,能让你的代码更稳定、内存更省、性能更好。

