
1. 什么是__slots__?
__slots__是Python中一个特殊的类属性,它允许开发者显式声明类实例可以拥有的属性,从而替代默认的__dict__字典存储方式。这个特性在Python 2.2中首次引入,主要目的是优化内存使用和属性访问速度。
基本语法
class MyClass:
__slots__ = ['attr1', 'attr2']
def __init__(self):
self.attr1 = 42
self.attr2 = "Hello"
2. __slots__的底层原理
2.1 默认情况下的实例属性存储
普通Python类实例的属性存储在__dict__字典中:
class RegularClass:
pass
obj = RegularClass()
obj.a = 1 # 存储在__dict__中
这种设计虽然灵活,但存在两个问题:
- 每个实例都需要维护一个字典,内存开销大
- 属性访问需要字典查找,速度较慢
2.2 使用__slots__后的变化
当定义了__slots__后:
- Python会为类创建描述符(descriptor)来管理属性
- 实例不再拥有
__dict__属性 - 属性访问变为直接偏移量访问,类似C结构体
内存布局对比:
普通对象: [PyObject_HEAD][__dict__指针] -> {属性字典}
Slots对象: [PyObject_HEAD][attr1][attr2]
3. 性能对比测试
3.1 内存占用测试
import sys
class Regular: pass
class Slotted:
__slots__ = ['x', 'y', 'z']
r = Regular()
s = Slotted()
print(f"Regular实例大小: {sys.getsizeof(r) + sys.getsizeof(r.__dict__)} bytes")
print(f"Slotted实例大小: {sys.getsizeof(s)} bytes")
典型输出结果:
Regular实例大小: 152 bytes
Slotted实例大小: 48 bytes
3.2 属性访问速度测试
import timeit
regular_setup = 'obj = Regular(); obj.x = 1'
slotted_setup = 'obj = Slotted(); obj.x = 1'
t1 = timeit.timeit('obj.x = 2', setup=regular_setup)
t2 = timeit.timeit('obj.x = 2', setup=slotted_setup)
print(f"常规属性设置: {t1:.2f}秒")
print(f"Slots属性设置: {t2:.2f}秒")
4. 使用场景与最佳实践
4.1 适用场景
- 需要创建大量实例的类(如ORM模型)
- 属性固定的简单数据结构
- 对内存使用敏感的应用(如嵌入式系统)
- 需要高性能属性访问的场景
4.2 注意事项
-
继承问题:
- 如果子类不定义
__slots__,它会自动获得__dict__ - 如果子类定义了
__slots__,它会继承父类的__slots__
- 如果子类不定义
-
与描述符的交互:
class Demo: __slots__ = ['_x'] @property def x(self): return self._x @x.setter def x(self, value): self._x = value -
动态添加属性:
- 默认情况下无法动态添加
__slots__未声明的属性 - 可以通过添加
'__dict__'到__slots__来保留该功能
- 默认情况下无法动态添加
5. 高级技巧与陷阱
5.1 与weakref的配合
import weakref
class WeakableSlotted:
__slots__ = ['__weakref__', 'data']
5.2 多继承问题
当多个父类都有__slots__时,子类需要显式声明:
class BaseA:
__slots__ = ['a']
class BaseB:
__slots__ = ['b']
class Child(BaseA, BaseB):
__slots__ = ['c'] # 必须显式声明
5.3 Pickle支持
默认情况下,__slots__类可以被pickle,但需要确保所有属性都是可pickle的。
6. 真实案例:Django中的__slots__
Django框架中大量使用__slots__来优化模型字段:
# django/db/models/fields/__init__.py
class Field:
__slots__ = (
'name', 'verbose_name', 'primary_key', 'max_length',
'unique', 'blank', 'null', 'db_index', 'default',
# ... 约20个预定义属性
)
7. 总结
| 特性 | 使用__dict__ | 使用__slots__ |
|---|---|---|
| 内存使用 | 较高 | 较低 |
| 属性访问速度 | 较慢 | 较快 |
| 动态添加属性 | 支持 | 有限支持 |
| 继承复杂性 | 简单 | 需要特别注意 |
最佳实践建议:
- 在确定属性固定的情况下使用
- 避免在需要动态添加属性的类中使用
- 注意继承时的
__slots__声明 - 对于性能关键代码,先测量再决定
5320

被折叠的 条评论
为什么被折叠?



