为什么我在尝试挑选一个对象时遇到定义__slots__的类的错误?

时间:2010-02-05 00:00:58

标签: python pickle slots

我正在尝试挑选我定义的(新式)类的对象。但我收到以下错误:

>>> with open('temp/connection.pickle','w') as f:
...   pickle.dump(c,f)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/usr/lib/python2.5/pickle.py", line 1362, in dump
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.5/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.5/pickle.py", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib/python2.5/pickle.py", line 419, in save_reduce
    save(state)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "/usr/lib/python2.5/pickle.py", line 663, in _batch_setitems
    save(v)
  File "/usr/lib/python2.5/pickle.py", line 306, in save
    rv = reduce(self.proto)
  File "/usr/lib/python2.5/copy_reg.py", line 76, in _reduce_ex
    raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled

我没有在班上明确定义__slots__。我做了什么隐含地定义它?我该如何解决这个问题?我是否需要定义__getstate__

更新: gnibbler选择了一个很好的例子。我试图挑选的对象的类包装了一个套接字。 (现在发生在我身上)套接字有充分的理由定义__slots__而不是__getstate__。我假设一旦进程结束,另一个进程就无法解开并使用前一进程的套接字连接。因此,虽然我接受Alex Martelli的优秀答案,但我将不得不采取不同的策略,而不是挑选“共享”对象参考。

3 个答案:

答案 0 :(得分:29)

定义__slots__(而不是__getstate__)的类可以是您的祖先类,也可以是您的属性或项目的类(或祖先类),直接或间接:基本上,以对象为根的引用的有向图中的任何对象的类,因为pickling需要保存整个图。

对您的困境的一个简单解决方案是使用协议-1,这意味着“最好的协议泡菜可以使用”;默认值是一种古老的基于ASCII的协议,它对__slots__ vs __getstate__施加了此限制。考虑:

>>> class sic(object):
...   __slots__ = 'a', 'b'
... 
>>> import pickle
>>> pickle.dumps(sic(), -1)
'\x80\x02c__main__\nsic\nq\x00)\x81q\x01.'
>>> pickle.dumps(sic())
Traceback (most recent call last):
  [snip snip]
    raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
>>> 

如您所见,协议-1大步采用__slots__,而默认协议则提供您看到的相同异常。

协议-1的问题:它产生二进制字符串/文件,而不是像默认协议那样的ASCII字符串/文件;足够古老的Python版本无法加载生成的pickle文件。除了关键词__slots__之外,优点包括更紧凑的结果和更好的性能。

如果您被迫使用默认协议,那么您需要确切地确定哪个类给您带来麻烦以及确切原因。如果是这种情况我们可以讨论策略(但是如果你可以使用-1协议,那就更好了,不值得讨论;-)并且简单的代码检查也在寻找麻烦的类/对象复杂(我想到了一些基于深度复制的技巧,以获得整个图形的可用表示,以防你想知道)。

答案 1 :(得分:6)

您的实例的属性可能正在使用__slots__

例如,socket__slots__因此无法腌制

您需要确定导致错误的属性并编写自己的属性 __getstate____setstate__忽略该属性

答案 2 :(得分:2)

来自PEP 307

  

__getstate__方法应返回可选值         表示对象的状态而不引用对象         本身。如果不存在__getstate__方法,则为默认值         使用返回self.__dict__

的实现