欢迎光临
我们一直在努力

如何通过 __slots__ 显著降低 Python 类实例的内存开销

Python以其简洁和动态性而闻名,但这种灵活性是有代价的:内存占用通常较高。默认情况下,Python中的每个类实例都会维护一个内部的字典(__dict__)来存储其属性。虽然这允许在运行时动态添加属性,但对于需要创建数百万个轻量级对象的应用场景(如游戏开发、数据处理),这个额外的字典开销是巨大的负担。

__slots__ 提供了一个简单的解决方案,允许程序员告诉Python解释器,某个类的实例将只拥有固定的属性集,从而绕过对 __dict__ 的需求。

slots 的工作原理

当你在类中定义 __slots__ 属性时,Python不再为实例分配一个字典,而是使用一个固定大小的内部结构(类似于C语言的数组或结构体)来存储这些命名属性。这极大地减少了单个实例的内存开销,并且通常能略微提高属性访问速度。

实践:内存开销对比

我们使用 sys.getsizeof() 来比较普通类和使用了 __slots__ 的类实例在内存上的差异。请注意,sys.getsizeof() 测量的是对象本身的开销,而不是其引用的所有数据,但它能清晰地展示出 __dict__ 带来的额外负担。

示例代码

import sys

# 1. 传统类 (默认使用 __dict__)
class PointDefault:
    """没有使用 __slots__ 的类"""
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 2. 使用 __slots__ 的类
class PointSlotted:
    """通过 __slots__ 限制属性,避免创建 __dict__"""
    __slots__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 实例化对象
default_p = PointDefault(10, 20)
slotted_p = PointSlotted(10, 20)

# 测量实例大小
default_size = sys.getsizeof(default_p)
slotted_size = sys.getsizeof(slotted_p)

print(f"--- 内存占用对比 (Python {sys.version.split(' ')[0]}) ---")
print(f"传统类 (PointDefault) 实例大小: {default_size} 字节")
print(f"使用 __slots__ (PointSlotted) 实例大小: {slotted_size} 字节")

# 尝试访问 __dict__
print("\n--- __dict__ 检查 ---")
print(f"传统类是否有 __dict__: {'__dict__' in default_p.__dir__()})")
try:
    # 尝试访问 slotted 对象的 __dict__ 会失败
    slotted_p.__dict__
except AttributeError:
    print(f"Slotted 类没有 __dict__ (验证成功)")

# 内存节省率 (以 Python 3.10+ 环境为例,通常能节省 40% - 60%)
print(f"\n内存节省率 (粗略): {1 - (slotted_size / default_size):.2%}")

运行结果分析 (示例输出)

在大多数64位Python环境中,传统类实例通常会占用80字节以上(因为需要容纳 __dict__ 的指针及少量开销),而使用了 __slots__ 的实例可能只占用48到56字节。内存节省率通常可以达到40%甚至更高。

使用 slots 的限制与注意事项

虽然 __slots__ 是一个强大的内存优化工具,但它并非没有代价:

  1. 禁止动态属性添加: 一旦使用 __slots__,你将无法在运行时给实例添加未在 __slots__ 中定义的属性。
  2. 没有 **__dict____weakref__:** 默认情况下,实例不再拥有 __dict__(这是内存优化的来源),并且不能被弱引用。
  3. 继承复杂性: 如果子类没有定义 __slots__,那么子类实例将重新拥有 __dict__。如果子类定义了 __slots__,它必须包含父类中所有 slots 定义的属性,并且不能重复。
【本站文章皆为原创,未经允许不得转载】:汤不热吧 » 如何通过 __slots__ 显著降低 Python 类实例的内存开销
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址