首页 > 解决方案 > 不能用递归引用腌制对象

问题描述

我想创建一组这些对象并腌制/解开它们。但是,如果存在递归引用,例如:

mdp1 = MDP(0, (0,1))
mdp2 = MDP(0, (0,2))
mdp1.adj = frozenset({mdp2})
mdp2.adj = frozenset({mdp1})
mdps = {mdp1, mdp2}
p_on = open("test.pickle", "wb")
pickle.dump(mdps, p_on)
p_on.close()

p_off = open("test.pickle", "rb")
emp = pickle.load(p_off)
print(emp)

在这种情况下mdp1.adjcontainsmdp2mdp2contains mdp1

作为一个最小的可重现示例:

import pickle


    class MDP(object):
        def __init__(self, level, state_var):
            self.state_var = state_var
            self.level = level
            self.adj = frozenset()
            self.mer = frozenset()
            ...

        def __repr__(self):
           return "level {} var {} mer {}".format(self.level, self.state_var, self.mer)
    
        def __eq__(self, other):
            if isinstance(other, MDP):
                return (self.level == other.level and self.state_var == other.state_var and self.mer==other.mer)
            else:
                return False
    
        def __hash__(self):
            return hash(self.__repr__())
    
        def __lt__(self, other):
            return self.state_var < other.state_var

    def main():
        mdp1 = MDP(0, (0,1))
        mdp2 = MDP(0, (0,2))
        mdp1.adj = frozenset({mdp2})
        mdp2.adj = frozenset({mdp1})
        mdps = {mdp1, mdp2}
        p_on = open("test.pickle", "wb")
        pickle.dump(mdps, p_on)
        p_on.close()

        p_off = open("test.pickle", "rb")
        emp = pickle.load(p_off)
        print(emp)

    main()

我得到以下堆栈跟踪:

Traceback (most recent call last):
  File "../test.py", line 43, in <module>
    main()
  File "../test.py", line 39, in main
    emp = pickle.load(p_off)
  File "../test.py", line 22, in __hash__
    return hash(self.__repr__())
  File "../test.py", line 13, in __repr__
    return "level {} var {} mer {}".format(self.level, self.state_var, self.mer)
AttributeError: 'MDP' object has no attribute 'level'

标签: pythonrecursionpickle

解决方案


对于任何有相同问题的人,user2357112帮助解释了在 unpickling 过程中:由于循环引用,在 unpickling 过程中,一些对象将不得不看到其他对象处于未初始化或部分初始化状态,并且 pickle 不知道什么情况那是安全的还是不安全的。它最终尝试在设置级别之前将 MDP 实例添加到调整集。

这是因为我在课堂上定义散列函数和相等性的方式。删除这些自定义定义并让 python 处理相等和散列解决了我的问题。Pickle 能够使用循环引用序列化对象:https ://pymotw.com/2/pickle/#circular-references


推荐阅读