9

考虑以下 MWE:

#import dill as pickle      # Dill exhibits similar behavior
import pickle

class B:
    def __init__(self):
        self.links = set()

class A:
    def __init__(self, base: B):
        self.base = base
        base.links.add(self)

    def __hash__(self):
        return hash(self.base)

    def __eq__(self, other):
        return self.base == other.base

pickled = pickle.dumps(A(B()))  # Success
print(pickle.loads(pickled))    # Not so much

上面的示例失败,但出现以下异常:

Traceback (most recent call last):
  File "./mwe.py", line 26, in <module>
    print(pickle.loads(pickled))
  File "./mwe.py", line 18, in __hash__
    return hash(self.base)
AttributeError: 'A' object has no attribute 'base'

B.links据我了解这个问题,pickle 在反序列化之前尝试反序列化Aset用于B尝试在某个点调用的A.__hash__实例,并且由于实例A尚未完全构造,它无法计算自己的哈希,这让每个人都感到难过。

如何在不破坏循环引用的情况下解决这个问题?(打破循环将是很多工作,因为我试图序列化的对象非常复杂)

4

1 回答 1

8

我认为您已经正确地确定了问题的原因。两个实例都依赖于另一个,并且pickle无法以正确的顺序初始化它们。这可能被认为是一个错误,但幸运的是,有一个简单的解决方法。

Pickle 允许我们使用__getstate____setstate__函数自定义对象的腌制方式。我们可以使用它在散列之前手动设置实例的缺失base属性:A

class B:
    def __init__(self):
        self.links = set()

    def __getstate__(self):
        # dump a tuple instead of a set so that the __hash__ function won't be called
        return tuple(self.links)

    def __setstate__(self, state):
        self.links= set()
        for link in state:
            link.base= self # set the missing attribute
            self.links.add(link) # now it can be hashed
于 2017-07-03T14:27:16.390 回答