Python 10天冲刺《__slots__ 是一个类级属性》用于限制和优化对象的属性存储
在 Python 中,__slots__
是一个类级属性,用于限制和优化对象的属性存储。通过定义 __slots__
,可以显著减少对象的内存占用,并提升属性访问的性能。以下是关于 __slots__
的详细解释和使用示例:
1. 基本概念
- 作用:
__slots__
告诉 Python 解释器,一个类的实例只能拥有特定的属性(即声明在__slots__
中的属性),而不再使用__dict__
(对象的默认属性字典)来存储属性。 - 目的:
主要用于优化内存和性能,适用于大量实例化对象的场景(如数据结构、持久化存储等)。
2. 基础用法
定义 __slots__
在类中定义 __slots__
,并指定允许的属性名称:
class Point:__slots__ = ('x', 'y') # 或者用元组或列表:__slots__ = ['x', 'y']def __init__(self, x, y):self.x = x # 允许访问的属性self.y = y
限制属性
尝试添加未声明的属性会引发错误:
p = Point(1, 2)
p.z = 3 # 抛出 AttributeError: 'Point' object has no attribute 'z'
3. 核心原理
替代 __dict__
- 普通类:对象默认使用
__dict__
字典存储所有属性,灵活性高,但内存开销大。 - 使用
__slots__
:对象属性直接存储在固定的位置(类似 C 结构体的字段),无需字典,节省内存。
import sysclass WithoutSlots:def __init__(self, x, y):self.x = xself.y = yclass WithSlots:__slots__ = ('x', 'y')def __init__(self, x, y):self.x = xself.y = y# 计算内存占用
print(sys.getsizeof(WithoutSlots(1, 2))) # 可能输出: 64 字节(包含 __dict__)
print(sys.getsizeof(WithSlots(1, 2))) # 可能输出: 40 字节(无 __dict__)
性能提升
- 属性访问更快:因为属性直接通过指针访问,而不是字典查找。
- 节省内存:尤其在创建大量对象时效果显著。
4. 高级用法
1. 继承与 __slots__
-
如果子类未定义
__slots__
,则无法使用父类的__slots__
,需显式声明:class Base:__slots__ = ('x', 'y')class Derived(Base):__slots__ = () # 必须显式声明,否则会抛出错误
-
子类可以扩展父类的
__slots__
:class Derived(Base):__slots__ = ('z',) # 新增 'z' 属性
2. 使用 __slots__
和 __dict__
混合
- 如果需要保留
__dict__
的灵活性,可以将'__dict__'
加入__slots__
:class MyClass:__slots__ = ('x', '__dict__') # 允许动态添加其他属性def __init__(self):self.y = 2 # 允许,因为有 __dict__
3. 元类与 __slots__
- 元类中可以通过
__slots__
控制属性:class Meta(type):__slots__ = () # 限制元类自身的属性class MyClass(metaclass=Meta):__slots__ = ('x',)
5. 注意事项
限制
- 不能动态添加属性:只能使用
__slots__
中声明的属性。 - 无法使用
__dict__
:如果未显式声明'__dict__'
,则对象没有__dict__
属性。 - 不适用于多继承:如果父类中有多个类定义了
__slots__
,需要将它们的__slots__
合并到子类中。class A: __slots__ = ('a',) class B: __slots__ = ('b',) class AB(A, B): __slots__ = () # 需要合并父类的 slots:('a', 'b')
常见问题
-
如何查看已声明的
__slots__
:print(Point.__slots__) # 输出: ('x', 'y')
-
实例是否有
__dict__
:p = Point(1, 2) print(hasattr(p, '__dict__')) # 输出:False(如果未显式声明 '__dict__')
6. 使用场景
- 内存密集型场景:例如存储大量数据对象(如数据库记录、日志条目)。
- 高性能需求:需要频繁访问属性时,提升访问速度。
- 防止属性滥用:限制对象只能拥有特定属性,避免意外修改或添加属性。
7. 示例对比
普通类 vs 使用 __slots__
import sysclass WithoutSlots:def __init__(self, x, y):self.x = xself.y = yclass WithSlots:__slots__ = ('x', 'y')def __init__(self, x, y):self.x = xself.y = y# 内存对比
print(sys.getsizeof(WithoutSlots(1, 2))) # 64 bytes(带__dict__)
print(sys.getsizeof(WithSlots(1, 2))) # 40 bytes(无__dict__)# 访问速度对比
import timeitdef test_normal():obj = WithoutSlots(1, 2)for _ in range(1000):obj.x += 1def test_slots():obj = WithSlots(1, 2)for _ in range(1000):obj.x += 1print("Normal class: ", timeit.timeit(test_normal, number=100000))
print("Slots class: ", timeit.timeit(test_slots, number=100000))
8. 总结
- 优点:
- 减少内存占用(尤其适合大量对象)。
- 提升属性访问速度。
- 避免意外添加属性,增强代码安全性。
- 缺点:
- 灵活性降低,无法动态添加属性。
- 多继承时需要谨慎处理
__slots__
的合并。
- 适用场景:数据结构、高性能计算、内存敏感的项目。
通过合理使用 __slots__
,可以在 Python 中实现更高效、更可控的对象管理。