【问题标题】:How to pickle and unpickle instances of a class that inherits from defaultdict?如何腌制和取消腌制从 defaultdict 继承的类的实例?
【发布时间】:2010-10-04 12:57:23
【问题描述】:

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

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

我可以腌制它,但是当我解开它时,会发生这种情况:

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

该类没有定义pickle协议的任何特殊方法。正常defaultdict(list) 的酸洗和解酸按预期工作。谁能赐教?

【问题讨论】:

  • 您使用的是pickle 还是cpickle

标签: python pickle


【解决方案1】:

类型通过定义一组(相当大的)方法中的一个或多个来定义它的实例如何被腌制。每个人都有自己微妙的行为。见the docs on the pickle protocol。对于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__ 方法采用 optional 可调用的,默认为 list,或者您可以只使用函数而不是类:

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

【讨论】:

  • 谢谢!我决定使用默认参数,因为这似乎是最简单、最稳定的选项。
  • 真的吗?该功能更简单:)
  • 谢谢。这帮助我解决了使用[0] * self._size 作为默认值的问题,并让_size 参数在酸洗过程中持续存在。
【解决方案2】:

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

您的类继承自 defaultdict,并定义了一个初始化程序。此初始化程序调用 defaultdict 的初始化程序并将 'list' 传递给它,在这种情况下,它可能是一个函数或一个类。 (我懒得去查了)。

您可能的意思是这样做:

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

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

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

【讨论】:

  • list 是一个类。 OP 没有传递一个特定的列表,而是作为一个可调用的类,它作为其自身的工厂函数,这是 defaultdict 的构造函数作为参数的。让listdictlist 作为参数违背了继承defaultdict 的目的。
  • 它一定会时不时发生
  • 然而,问题是“这是故意的吗?”他可能一直在对其进行测试,并计划稍后在课程中添加更多功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多