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

发布于 2024-08-21 01:22:42 字数 1644 浏览 5 评论 0原文

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

>>> 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 选择了一个很好的例子。我试图 pickle 的对象的类包装了一个套接字。 (我现在想到)套接字定义 __slots__ 而不是 __getstate__ 是有充分理由的。我假设一旦一个进程结束,另一个进程就无法取消并使用前一个进程的套接字连接。因此,虽然我接受 Alex Martelli 的出色答案,但我将不得不采取不同的策略而不是通过酸洗来“共享”对象引用。

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

Update: gnibbler chose 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

暗喜 2024-08-28 01:22:42

定义 __slots__ 的类(而不是 < a href="https://docs.python.org/2/library/pickle.html#object.__getstate__" rel="noreferrer">__getstate__) 可以是祖先您的类,或者您的属性或项目的类(或祖先类),直接或间接:本质上,是以您的对象为根的引用有向图中中任何对象的类,因为 pickling 需要保存整个图。

解决您的困境的一个简单方法是使用协议 -1,这意味着“pickle 可以使用的最佳协议”;默认值是一个古老的基于 ASCII 的协议,它对 __slots____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 字符串/文件;生成的 pickle 文件将无法被足够古老的 Python 版本加载。除了与 __slots__ 相关的关键优势之外,其优点还包括更紧凑的结果和更好的性能。

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

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 graph of references with your object as root, since pickling needs to save the entire graph.

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:

>>> 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 -1 takes the __slots__ in stride, while the default protocol gives the same exception you saw.

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.

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 -1 protocol, 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).

痴者 2024-08-28 01:22:42

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

例如,socket__slots__ 因此它无法被 pickle

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

Perhaps an attribute of your instance is using __slots__

For example, socket has __slots__ so it can't be pickled

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

水中月 2024-08-28 01:22:42

来自 PEP 307

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

From 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__.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文