在Python类定义中使用slots可以限制实例的属性,同时改变实例的属性存储方式,很多开发者会疑惑定义slots之后,类的实例是否还会拥有dict属性,以及这种用法能带来多少内存节省。实际上slots对实例dict的影响和内存优化的效果,都和slots的具体定义方式、实例属性的使用情况直接相关。
slots定义后实例是否还有dict
默认情况下,Python类的实例会有一个__dict__属性,用来存储实例的动态属性,这个字典也是实例占用内存较多的重要原因之一。当我们给类定义__slots__时,实例的__dict__存在情况分为两种:
仅定义普通属性名到slots
如果__slots__中只包含字符串形式的属性名,没有包含'__dict__',那么实例默认不会拥有__dict__属性,此时实例不能动态添加slots之外的属性。
class NormalSlots:
__slots__ = ('name', 'age')
obj = NormalSlots()
obj.name = "test"
# 下面这行会报错,因为没有__dict__,不能动态添加属性
# obj.gender = "male"
print(hasattr(obj, '__dict__')) # 输出 False
slots中包含'__dict__'
如果__slots__的定义中显式包含了'__dict__',那么实例仍然会拥有__dict__属性,此时实例可以动态添加slots之外的属性,不过这种情况下slots的内存优化效果会打折扣。
class SlotsWithDict:
__slots__ = ('name', 'age', '__dict__')
obj = SlotsWithDict()
obj.name = "test"
obj.gender = "male" # 可以动态添加属性
print(hasattr(obj, '__dict__')) # 输出 True
print(obj.__dict__) # 输出 {'gender': 'male'}
slots能节省多少内存
slots节省内存的核心原因是,它让实例不再使用__dict__存储属性,而是使用固定大小的数组存储,减少了字典的开销。我们可以通过sys.getsizeof来测算不同情况下实例的内存占用。
测试代码示例
import sys
class NoSlots:
def __init__(self, name, age):
self.name = name
self.age = age
class WithSlots:
__slots__ = ('name', 'age')
def __init__(self, name, age):
self.name = name
self.age = age
# 创建实例
no_slots_obj = NoSlots("张三", 20)
with_slots_obj = WithSlots("张三", 20)
# 计算实例本身的内存占用,注意getsizeof不会递归计算引用对象的大小
print(f"无slots实例内存: {sys.getsizeof(no_slots_obj)} 字节")
print(f"有slots实例内存: {sys.getsizeof(with_slots_obj)} 字节")
测试结果说明
在64位Python环境下,上述测试的输出通常类似如下:
- 无slots实例内存: 56 字节
- 有slots实例内存: 24 字节
可以看到单个实例就能节省32字节左右的内存,如果程序中需要创建成千上万个同类型的实例,内存节省的效果会非常明显。如果slots中包含了'__dict__',那么实例的内存占用会介于两者之间,因为既要存储固定数组,也要存储字典的部分开销。
slots使用的注意事项
虽然slots能优化内存,但也不是所有场景都适合使用:
- slots定义的类继承时需要注意,子类如果没有定义
__slots__,会自动拥有__dict__,之前的优化效果会失效。 - slots会限制实例的动态属性添加,除非显式把
'__dict__'加入slots,否则不能给实例添加未定义的属性。 - 对于属性很少、实例数量不多的类,使用slots带来的收益不大,反而会增加代码的复杂度。
总结来说,默认情况下定义slots后实例没有dict,只有在slots中显式加入'__dict__'时实例才会有dict;slots的内存节省效果和实例数量、属性数量相关,大量实例场景下能显著降低内存占用。
slots__dict__实例内存Python内存优化__slots__修改时间:2026-06-22 06:36:57