You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何带__slots__的对象pickle序列化后体积比不带的更大?

问题:使用__slots__后pickle序列化体积反而增大的原因

我正在开发的程序频繁因OOM killer崩溃,想在不进行大规模重构的前提下快速降低内存占用。给最常用的类添加__slots__后,却发现其pickle序列化后的体积反而增加了,这是为什么?

class Class:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c


class ClassSlots:
    __slots__ = ["a", "b", "c"]

    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

cases = [
    Class(1, 2, 3),
    ClassSlots(1, 2, 3),
    [Class(1, 2, 3) for _ in range(1000)],
    [ClassSlots(1, 2, 3) for _ in range(1000)]
]

for case in cases:
    dump = pickle.dumps(case, protocol=5)
    print(len(dump))

在Python 3.10中运行上述代码,输出如下:

59
67
22041
25046

原因解析

这是因为普通类的实例在pickle序列化时,默认会打包存储实例的__dict__属性字典,而pickle协议5对字典序列化做了关键优化:对于重复出现的键(比如多个实例的a、b、c),只会存储一次键名,后续实例直接复用这个引用,大幅减少了重复数据的体积。

而使用__slots__的类,实例没有__dict__属性,pickle需要逐个序列化每个槽位的属性,并且每个实例都要重复存储槽位的属性名(即使多个实例的属性名完全一致)。这就导致单个实例的序列化体积更大,当序列化大量实例组成的列表时,重复的属性名会累积出更明显的体积差异。

简单总结:普通类靠字典的键复用优化压缩了体积,而__slots__类因为没有__dict__,吃不到这个优化,反而要重复存储属性名,所以序列化体积反而增大。


折中方案

如果既要保留__slots__的内存节省优势,又想优化pickle体积,可以尝试以下方法:

  • 自定义__getstate__和__setstate__方法,手动控制序列化内容,比如把槽位属性打包成元组或带共享键的字典,让pickle能复用属性名引用。示例代码:
    class ClassSlots:
        __slots__ = ["a", "b", "c"]
    
        def __init__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c
    
        def __getstate__(self):
            # 将槽位属性转为元组,减少序列化时的重复数据
            return (self.a, self.b, self.c)
    
        def __setstate__(self, state):
            self.a, self.b, self.c = state
    
    这样处理后,元组的序列化体积会远小于逐个存储属性名的方式,大量实例列表的序列化体积也会接近普通类的水平。
  • 升级到Python 3.11及以上版本,新版本的pickle对__slots__类的序列化做了针对性优化,能有效减少这类体积膨胀问题。

内容的提问来源于stack exchange,提问作者parched

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:40:10