如何pickle和unpickle继承自defaultdict的类的实例?

发布于 2024-09-26 03:56:07 字数 423 浏览 8 评论 0原文

我有一个继承自 defaultdict 的类,如下所示:

class listdict(defaultdict):
    def __init__(self):
        defaultdict.__init__(self, list)

我可以pickle它,但是当我取消pickle它时,会发生这种情况:

('__init__() takes exactly 1 argument (2 given)', <class 'listdict'>, (<type 'list'>,))

该类没有定义pickle协议的任何特殊方法。普通 defaultdict(list) 的酸洗和取消酸洗按预期工作。谁能启发我吗?

I have a class that inherits from defaultdict like this:

class listdict(defaultdict):
    def __init__(self):
        defaultdict.__init__(self, list)

I can pickle it, but when I unpickle it, this happens:

('__init__() takes exactly 1 argument (2 given)', <class 'listdict'>, (<type 'list'>,))

The class does not define any special methods of the pickle protocol. Pickling and unpickling of a normal defaultdict(list) works as expected. Can anyone enlighten me?

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

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

发布评论

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

评论(2

笑脸一如从前 2024-10-03 03:56:07

类型通过定义一组(相当大的)方法中的一个或多个来定义它的实例如何被腌制。每个人都有自己微妙的行为。请参阅有关 pickle 协议的文档。对于collections.defaultdict,它使用__reduce__方法:

>>> l = collections.defaultdict(list)
>>> l.__reduce__()
(<type 'collections.defaultdict'>, (<type 'list'>,), None, None, <dictionary-itemiterator object at 0x7f031fb3c470>)

元组中的第一项是类型,第二项是要传递的参数的元组实例化时的类型。如果您不覆盖 __reduce__,第一项将正确更改为您的类型,但第二项不会。这会导致您看到的错误。一个如何修复它的粗略示例:

>>> import collections
>>> import pickle
>>> class C(collections.defaultdict):
...     def __init__(self):
...         collections.defaultdict.__init__(self, list)
...     def __reduce__(self):
...         t = collections.defaultdict.__reduce__(self)
...         return (t[0], ()) + t[2:]
...
>>> c = C()
>>> c[1].append(2)
>>> c[2].append(3)
>>> c2 = pickle.loads(pickle.dumps(c))
>>> c2 == c
True

这只是一个粗略示例,因为需要进行酸洗(例如 __reduce_ex__),而且都相当复杂。在这种情况下,使用__getinitargs__可能会更方便。

或者,您可以使类的 __init__ 方法采用可选可调用,默认为 list,或者您可以只使用函数而不是类:

def listdict():
    return collections.defaultdict(list)

Types define how instances of it get pickled by defining one or more of a (fairly large) set of methods. Each has its own subtle behaviour. See the docs on the pickle protocol. In the case of collections.defaultdict, it uses the __reduce__ method:

>>> l = collections.defaultdict(list)
>>> l.__reduce__()
(<type 'collections.defaultdict'>, (<type 'list'>,), None, None, <dictionary-itemiterator object at 0x7f031fb3c470>)

The first item in the tuple there is the type, and the second item is the tuple of arguments to pass to the type when instantiating it. If you don't override __reduce__, the first item will correctly change to your type, but the second item will not. This causes the error you see. A crude example of how you could fix it:

>>> import collections
>>> import pickle
>>> class C(collections.defaultdict):
...     def __init__(self):
...         collections.defaultdict.__init__(self, list)
...     def __reduce__(self):
...         t = collections.defaultdict.__reduce__(self)
...         return (t[0], ()) + t[2:]
...
>>> c = C()
>>> c[1].append(2)
>>> c[2].append(3)
>>> c2 = pickle.loads(pickle.dumps(c))
>>> c2 == c
True

It's only a crude example because there's more to pickling (like __reduce_ex__) and it's all fairly intricate. In this case, using __getinitargs__ may be more convenient.

Alternatively, you could make your class's __init__ method take an optional callable, defaulting to list, or you could just use a function instead of a class:

def listdict():
    return collections.defaultdict(list)
不即不离 2024-10-03 03:56:07

该错误表明您的“listdict”类应该采用一个参数(隐式自我),但得到了两个参数。

您的类继承自defaultdict,并定义一个初始值设定项。该初始化程序调用 defaultdict 的初始化程序并将“list”传递给它,在本例中它可以是函数或类。 (我懒得去检查)。

您的意思可能是这样做:

class listdict(defaultdict):
    def __init__(self, list):
        defaultdict.__init__(self, list)

现在,当使用给定列表初始化 listdict 时,它将该列表传递给 defaultdict 的构造函数,而不是传递对全局列表的引用。

(也就是说,使用与常见全局方法和类相同的名称(例如“str”、“list”等)被认为是不好的风格,这与您感到困惑的原因相同)。

That error indicates that your 'listdict' class was expected to take one argument (the implicit self), but got two arguments.

Your class inherits from defaultdict, and defines an initializer. This initializer calls defaultdict's initializer and passes 'list' to it, which in this case may be either a function or a class. (I can't be bothered to check).

What you probably meant is to do this:

class listdict(defaultdict):
    def __init__(self, list):
        defaultdict.__init__(self, list)

Now, when listdict is initialised with a given list, it passes THAT list to the defaultdict's constructor, rather than passing a reference to the global list.

(That said, it's considered bad style to use a name that is the same as common global methods and classes, such as 'str', 'list', etc, for the same reason that you got confused).

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