挑选OrderedDict的子类

时间:2017-08-24 11:07:54

标签: python python-3.x pickle ordereddictionary

以下陈述:

import pickle
from collections import OrderedDict as Odict

class A(Odict):
    def __init__(self, items):
        super().__init__(items)

items = Odict((('a',1), ('b', 2)))
a = A(items)

with open('test.pickle','wb') as fout:
    pickle.dump(a, fout)

with open('test.pickle','rb') as fin:
    pickle.load(fin)

导致此错误:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: __init__() missing 1 required positional argument: 'items'

但使用普通dict而非OrderedDict工作正常。我知道在这种情况下我不需要__init__,但是这个问题阻止了使用多处理模块和更复杂的OrderedDict子类,其他参数作为属性存储,我可以& #39;避免拥有它。 (我使用的是python 3.4.6)。

1 个答案:

答案 0 :(得分:2)

OrderedDict会覆盖__reduce__,如果您覆盖__init____new__方法和/或想要存储其他属性,则需要覆盖它。

在您的情况下,您强制要求__init__的参数(dictOrderedDict不强制要求),因此您需要覆盖__reduce__

import collections

class OD(collections.OrderedDict):
    def __init__(self, items):
        super().__init__(items)

    def __reduce__(self):
        state = super().__reduce__()
        # OrderedDict.__reduce__ returns a 5 tuple
        # the first and last can be kept
        # the fourth is None and needs to stay None
        # the second must be set to an empty sequence
        # the third can be used to store attributes
        newstate = (state[0],
                    ([], ),
                    None,
                    None,
                    state[4])
        return newstate

现在可以毫无问题地进行腌制:

import pickle

a = OD((('a',1), ('b', 2)))

with open('test.pickle','wb') as fout:
    pickle.dump(a, fout)

with open('test.pickle','rb') as fin:
    pickle.load(fin)

但是,如果您想要在__init__中未设置的属性,这将无法正常工作:

a = OD((('a',1), ('b', 2)))
a.a = 10

with open('test.pickle','wb') as fout:
    pickle.dump(a, fout)

with open('test.pickle','rb') as fin:
    b = pickle.load(fin)

b.a  # AttributeError: 'OD' object has no attribute 'a'

要完成这项工作,您还需要更改上面提到的__reduce__函数以返回第三个参数。例如,您只需返回__dict__

即可
class OD(collections.OrderedDict):
    def __init__(self, items):
        super().__init__(items)

    def __reduce__(self):
        state = super().__reduce__()
        newstate = (state[0],
                    ([], ),
                    self.__dict__,
                    None,
                    state[4])
        return newstate

通过这个,上面的例子将正常工作。

许多设计取决于您希望子类的行为方式。在某些情况下,最好通过第二个参数(传递给__init__的参数)传递项目。至于如何设置属性:有时使用self.__dict__就足够了,但在其他情况下使用__setstate__会更安全/更好。你一定要阅读documentation of the pickle module并检查哪种方法最适合你。