Python Pickling插槽错误

11 投票
2 回答
5603 浏览
提问于 2025-04-16 03:01

我有一个很大的实例,之前一直能顺利保存,但最近在尝试保存的时候遇到了一个错误:

  File "/usr/lib/python2.6/copy_reg.py", line 77, in _reduce_ex
    raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled

我不太明白这个错误,因为我所有的类似乎都定义了一个 __getstate__ 方法,而且都没有定义 __slots__。我很难找出导致这个错误的具体改动。

我只能猜测,可能是我实例里面某个深层嵌套的对象引起了这个问题。有没有什么办法可以获取更多信息?我该如何找到导致这个错误的具体对象的类?

2 个回答

1

我也遇到过这个问题,不过是关于一些已经用旧的ASCII协议处理过的数据。你可以用这些方法来调整插槽,使其适应你的对象:

class MyAlreadyPickeldObjectWithslots(object):
    ___slots__= ("attr1","attr2",....)
    def __getstate__(self):
        return dict([(k, getattr(self,k,None)) for k in self.__slots__])

    def __setstate__(self,data):
        for k,v in data.items():
            setattr(self,k,v)

这样做可以帮助你节省一些内存。

13

使用二进制协议来进行数据序列化(也就是“腌制”数据),而不是你现在默认使用的旧的ASCII协议,这样就没问题了。看看下面的例子:

>>> class ws(object):
...   __slots__ = 'a', 'b'
...   def __init__(self, a=23, b=45): self.a, self.b = a, b
... 
>>> x = ws()
>>> import pickle
>>> pickle.dumps(x, -1)
'\x80\x02c__main__\nws\nq\x00)\x81q\x01N}q\x02(U\x01aq\x03K\x17U\x01bq\x04K-u\x86q\x05b.'
>>> pickle.dumps(x)
Traceback (most recent call last):
    [[snip]]
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy_reg.py", line 77, in _reduce_ex
    raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
>>> 

如你所见,-1协议(意思是“最佳、最快和最紧凑的协议”)运行得很好,而默认的0协议(这是为了兼容早到Python 1.5及之前版本的旧ASCII协议)则会引发你观察到的那个异常。

而且,-1不仅会更快,还会产生更小的结果——你只需要确保正确保存和恢复它生成的二进制字符串(比如,如果你要把数据保存到文件,记得要以wb模式打开文件,而不是仅仅用w模式)。

如果因为某种原因你无法使用这个全面获胜的解决方案,还有一些小技巧(例如,创建一个pickle.Pickler的子类,直接使用你自己子类的实例,而不是基础类的实例,就像pickle.dumps那样,重写save方法,以便在调用父类方法之前先追踪type(obj)),但如果可能的话,升级到正确的、最新的协议(-1在任何给定的Python版本中都是该版本支持的最先进的协议)总是个好主意。

撰写回答