解开字典的子类时发生AttributeError

时间:2019-02-15 10:36:39

标签: python pickle subclassing

在Python 3.5中,我做了一个非常简单的dict子类,它使用同义键(存储为字典属性):

class synonymDict(dict):

    def __init__(self, synonyms=None, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.synonyms = {} if synonyms is None else synonyms

    def __getitem__(self, name):
        return dict.__getitem__(self, self.synonyms.get(name, name))

    def __setitem__(self, name, value):
        dict.__setitem__(self, self.synonyms.get(name, name), value)

    def __repr__(self):
        return 'synonymDict(%s)' % dict.__repr__(self)

现在,想象一下我在myPackage.myTools中定义了这个类; 我尝试了酸洗和酸洗(用于多处理),但是出现以下属性错误:

>>> from myPackage import myTools
>>> synonyms = dict(A='a', B='b')
>>> mymapped = myTools.synonymDict(synonyms)
>>> mymapped['A'] = 36
>>> mymapped
synonymDict({'a': 36})
>>> mymapped.synonyms
{'A': 'a', 'B': 'b'}
>>> import pickle
>>> pickle.loads(pickle.dumps(mymapped))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/users/ldog/glouvel/install/python3/LibsDyogen/myPhylTree.py", line 35, in __setitem__
    dict.__setitem__(self, self.synonyms.get(name, name), value)
AttributeError: 'synonymDict' object has no attribute 'synonyms'

为什么在去渍过程中出现此错误?我应该复制synonyms的副本吗?是子类问题还是名称空间问题?

1 个答案:

答案 0 :(得分:0)

根据此答案https://stackoverflow.com/a/46560454/4614641,我可以用一行代码解决问题:定义默认的synonyms class 属性。

新代码:

class synonymDict(dict):
    synonyms = {}  # Needed for unpickling!

    def __init__(self, synonyms=None, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.synonyms = {} if synonyms is None else synonyms

    def __getitem__(self, name):
        return dict.__getitem__(self, self.synonyms.get(name, name))

    def __setitem__(self, name, value):
        dict.__setitem__(self, self.synonyms.get(name, name), value)

    def __repr__(self):
        return 'synonymDict(%s)' % dict.__repr__(self)

这是因为解开(请参见https://docs.python.org/3.5/library/pickle.html#pickling-class-instances)不会调用该类的__init__方法,而是会尝试先调用__setitem__ !我已经重写了该方法,以调用仅在__init__ ...

中定义的属性