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 sys

class WithoutSlots:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class WithSlots:
    __slots__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.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 sys

class WithoutSlots:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class WithSlots:
    __slots__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 内存对比
print(sys.getsizeof(WithoutSlots(1, 2)))  # 64 bytes(带__dict__)
print(sys.getsizeof(WithSlots(1, 2)))     # 40 bytes(无__dict__)

# 访问速度对比
import timeit

def test_normal():
    obj = WithoutSlots(1, 2)
    for _ in range(1000):
        obj.x += 1

def test_slots():
    obj = WithSlots(1, 2)
    for _ in range(1000):
        obj.x += 1

print("Normal class: ", timeit.timeit(test_normal, number=100000))
print("Slots class:  ", timeit.timeit(test_slots, number=100000))

8. 总结

  • 优点
    • 减少内存占用(尤其适合大量对象)。
    • 提升属性访问速度。
    • 避免意外添加属性,增强代码安全性。
  • 缺点
    • 灵活性降低,无法动态添加属性。
    • 多继承时需要谨慎处理 __slots__ 的合并。
  • 适用场景:数据结构、高性能计算、内存敏感的项目。

通过合理使用 __slots__,可以在 Python 中实现更高效、更可控的对象管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值