python 为什么在尝试pickle对象时会收到关于我的类定义__slots__的错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2204155/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-04 00:03:50  来源:igfitidea点击:

Why am I getting an error about my class defining __slots__ when trying to pickle an object?

pythonpickleslots

提问by Daryl Spitzer

I'm trying to pickle an object of a (new-style) class I defined. But I'm getting the following error:

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

>>> 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

I didn't explicitly define __slots__in my class. Did something I do implicitly define it? How do I work around this? Do I need to define __getstate__?

我没有__slots__在我的班级中明确定义。我所做的事情是否隐含地定义了它?我该如何解决这个问题?我需要定义__getstate__吗?

Update:gnibblerchose a good example. The class of the object I'm trying to pickle wraps a socket. (It occurs to me now that) sockets define __slots__and not __getstate__for good reason. I assume once a process ends, another process can't unpickle and use the previous process's socket connection. So while I'm accepting Alex Martelli's excellent answer, I'm going to have to pursue a different strategy than pickling to "share" the object reference.

更新:gnibbler选择了一个很好的例子。我试图腌制的对象的类包装了一个套接字。(现在我突然想到)套接字定义__slots__并没有__getstate__充分的理由。我假设一旦一个进程结束,另一个进程就不能解开并使用前一个进程的套接字连接。因此,虽然我接受了Alex Martelli的出色回答,但我将不得不采用与酸洗不同的策略来“共享”对象引用。

回答by Alex Martelli

The class defining __slots__(and not __getstate__) can be either an ancestor class of yours, or a class (or ancestor class) of an attribute or item of yours, directly or indirectly: essentially, the class of any object in the directed graphof references with your object as root, since pickling needs to save the entire graph.

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

A simple solution to your quandary is to use protocol -1, which means "the best protocol pickle can use"; the default is an ancient ASCII-based protocol which imposes this limitation about __slots__vs __getstate__. Consider:

解决您的困境的一个简单方法是使用 protocol -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
>>> 

As you see, protocol -1takes the __slots__in stride, while the default protocol gives the same exception you saw.

如您所见,协议-1采取了__slots__大步前进,而默认协议给出了与您看到的相同的异常。

The issues with protocol -1: it produces a binary string/file, rather than an ASCII one like the default protocol; the resulting pickled file would not be loadable by sufficiently ancient versions of Python. Advantages, besides the key one wrt __slots__, include more compact results, and better performance.

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

If you're forced to use the default protocol, then you'll need to identify exactly which class is giving you trouble and exactly why. We can discuss strategies if this is the case (but if you can possibly use the -1protocol, that's so much better that it's not worth discussing;-) and simple code inspection looking for the troublesome class/object is proving too complicated (I have in mind some deepcopy-based tricks to get a usable representation of the whole graph, in case you're wondering).

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

回答by John La Rooy

Perhaps an attribute of your instance is using __slots__

也许您的实例的一个属性正在使用 __slots__

For example, sockethas __slots__so it can't be pickled

例如,socket__slots__所以它不能被腌制

You need to identify which attribute is causing the error and write your own __getstate__and __setstate__to ignore that attribute

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

回答by Joe Koberg

From PEP 307:

来自PEP 307

The __getstate__method should return a picklable value representing the object's state without referencing the object itself. If no __getstate__method exists, a default implementation is used that returns self.__dict__.

__getstate__方法应该返回一个表示对象状态的picklable值,而不引用对象本身。如果不__getstate__存在任何方法,则使用返回self.__dict__.