Python 对象的灵活性大家都知道,可以随时给对象添加属性:
class User:
pass
u = User()
u.name = "Alice"
u.age = 30
但这种灵活性的代价也很大,每个普通 Python 对象都有个 __dict__ 字典来存储属性,对象一多内存开销就上来了,这时候 __slots__ 就派上用场。
slots 到底在干什么
__slots__ 让你提前声明类会用到哪些属性:
class User:
__slots__ = ["name", "age"]
def __init__(self, name, age):
self.name = name
self.age = age
这样做之后对象就不会再创建 __dict__ 了。属性存储变成静态的,查找和赋值都快了,尤其是创建大量实例时内存占用能降不少。
底层存储机制的差异
普通对象把每个属性当作 __dict__ 里的键值对访问属性就要做哈希查找。而用了 __slots__ 之后每个属性在内存里有个固定位置,访问变成了直接的数组索引操作省掉了字典查找的开销。
说白了就是把 Python 对象存储搞得像 C 的结构体一样紧凑。
内存能省多少
看个对比:
class Normal:
def __init__(self):
self.a = 1
self.b = 2
class Slotted:
__slots__ = ["a", "b"]
def __init__(self):
self.a = 1
self.b = 2
如果要创建几百万个实例,用 __slots__ 的版本能少用 50-70% 的内存。
属性访问快主要是因为:省掉了字典查找、不用算哈希值、也没有额外的内存间接访问,属性访问时间一般能减少 20-40%。
使用限制
__slots__ 也不是完美的,有些限制得注意。
最明显的是不能随便加属性了:
u = User()
u.address = "NYC" # ❌ AttributeError
继承的时候也麻烦,子类得定义自己的 __slots__,而且混用带slots和不带slots的类要小心。多重继承更复杂只有slots名不冲突才行。
另外默认不支持弱引用,要用的话得在 __slots__ 里显式加上 __weakref__。
什么场景适合用
几个典型场景:处理大数据集时有几百万个对象;科学计算里的轻量数据结构、游戏引擎里的实体和粒子系统等等,总之就是那些对内存和速度敏感的地方。
一些实用技巧
配合类型提示用能让代码更清晰。可以和 @property 装饰器结合,该灵活的地方还是保持灵活。空类直接用 __slots__ = () 把开销降到最低。
总结
__slots__ 就是让你用灵活性换内存效率和更快的属性访问。对于高性能场景来说这是个必须掌握的优化手段。
就算项目暂时不缺内存,理解 __slots__ 本身也很有价值。它能让你明白 Python 对象是怎么存属性的、属性查找为什么有成本、Python 在灵活性和效率之间怎么权衡。
这些知识对系统设计、性能调优、排查问题都有帮助。
https://avoid.overfit.cn/post/c18314c17e0047358cb13c0a990067ae
作者:Elshad Karimov