Python 的魔法属性__slots__:内存优化与速度提升秘籍
在 Python 面向对象编程中,__slots__
是一个特殊的类属性,用于显式声明类实例允许拥有的属性,从而优化内存使用和提高访问速度。以下是详细解释和代码示例:
一、基本作用
- 限制实例属性
显式声明允许的属性名称,禁止实例动态添加其他属性 - 优化内存
实例不再使用__dict__
字典存储属性,改用更紧凑的数据结构 - 加速访问
属性访问速度提升约 20-30%
二、代码对比示例
1. 普通类(不使用 __slots__
)
class RegularUser:
def __init__(self, name, age):
self.name = name
self.age = age
# 可以动态添加新属性
user = RegularUser("Alice", 25)
user.email = "alice@example.com" # 允许
print(user.__dict__) # 输出: {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}
2. 使用 __slots__
的类
class SlotUser:
__slots__ = ('name', 'age') # 显式声明允许的属性
def __init__(self, name, age):
self.name = name
self.age = age
# 尝试添加未声明的属性会报错
user = SlotUser("Bob", 30)
try:
user.email = "bob@example.com" # 触发 AttributeError
except AttributeError as e:
print(f"错误: {str(e)}") # 输出: 'SlotUser' object has no attribute 'email'
三、内存优化对比
使用 sys.getsizeof
查看对象内存占用(需导入 sys
模块):
import sys
# 创建大量实例对比内存差异
regular_users = [RegularUser("User", i) for i in range(100000)]
slot_users = [SlotUser("User", i) for i in range(100000)]
print(f"普通类实例总内存: {sum(sys.getsizeof(u) for u in regular_users):,} bytes")
print(f"Slot类实例总内存: {sum(sys.getsizeof(u) for u in slot_users):,} bytes")
典型输出结果:
普通类实例总内存: 11,200,000 bytes
Slot类实例总内存: 7,200,000 bytes
四、继承中的注意事项
当存在继承关系时,子类需要显式定义 __slots__
:
class BaseUser:
__slots__ = ('user_id',)
class AdminUser(BaseUser):
__slots__ = ('permissions',) # 必须显式声明
def __init__(self, user_id, permissions):
super().__init__(user_id)
self.permissions = permissions
# 合并父类 slots
class SuperUser(BaseUser):
__slots__ = BaseUser.__slots__ + ('level',) # 正确做法
五、使用场景建议
场景 | 推荐使用 | 不建议使用 |
---|---|---|
需要创建大量实例(10万+) | ✅ | |
实例属性固定且数量少(<10) | ✅ | |
需要动态添加属性 | ❌ | |
使用弱引用(weakref) | ❌(需额外声明 __weakref__ ) |
六、注意事项
- 不可动态添加属性
需预先明确所有可能的属性 - 继承链处理
子类必须重新定义__slots__
- 与属性描述符冲突
如使用@property
需要特殊处理 - 第三方库兼容性
某些库(如 ORM)可能依赖__dict__
通过合理使用 __slots__
,可以在内存敏感的场景(如数据处理、游戏开发)中显著提升程序性能,但需权衡灵活性的损失。