Python Pickling插槽错误
我有一个很大的实例,之前一直能顺利保存,但最近在尝试保存的时候遇到了一个错误:
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版本中都是该版本支持的最先进的协议)总是个好主意。