如何对带自引用的对象和使用slots的类进行序列化和反序列化?

9 投票
1 回答
4839 浏览
提问于 2025-04-15 23:15

如何正确地将一个包含槽的类的对象进行序列化,尤其是当这个对象通过它的某个属性引用自己时?这里有一个简单的例子,展示了我目前的实现方式,但我不确定这是否完全正确:

import weakref
import pickle

class my_class(object):

    __slots__ = ('an_int', 'ref_to_self', '__weakref__')

    def __init__(self):
        self.an_int = 42
        self.ref_to_self = weakref.WeakKeyDictionary({self: 1})

    # How to best write __getstate__ and __setstate__?
    def __getstate__(self):

        obj_slot_values = dict((k, getattr(self, k)) for k in self.__slots__)
        # Conversion to a usual dictionary:
        obj_slot_values['ref_to_self'] = dict(obj_slot_values['ref_to_self'])
        # Unpicklable weakref object:
        del obj_slot_values['__weakref__']
        return obj_slot_values

    def __setstate__(self, data_dict):
        # print data_dict
        for (name, value) in data_dict.iteritems():
            setattr(self, name, value)
        # Conversion of the dict back to a WeakKeyDictionary:
        self.ref_to_self = weakref.WeakKeyDictionary(
            self.ref_to_self.iteritems())

你可以用以下方式来测试:

def test_pickling(obj):
    "Pickles obj and unpickles it.  Returns the unpickled object"

    obj_pickled = pickle.dumps(obj)
    obj_unpickled = pickle.loads(obj_pickled)

    # Self-references should be kept:
    print "OK?", obj_unpickled == obj_unpickled.ref_to_self.keys()[0]
    print "OK?", isinstance(obj_unpickled.ref_to_self,
                            weakref.WeakKeyDictionary)

    return obj_unpickled

if __name__ == '__main__':
    obj = my_class()
    obj_unpickled = test_pickling(obj)
    obj_unpickled2 = test_pickling(obj_unpickled)

这个实现方式是否正确且稳健?如果my_class是从一个有__slots__的类继承而来的,__getstate____setstate__应该怎么写?在__setstate__里面是否因为“循环”字典而存在内存泄漏的问题?

PEP 307中有一句话让我对是否能以稳健的方式序列化my_class对象产生了疑问:

__getstate__方法应该返回一个可序列化的值,表示对象的状态,而不应该引用对象本身。

这是否与对象本身的引用被序列化的事实相冲突?

问题真不少:任何评论、建议或意见都非常感谢!

1 个回答

6

看起来原帖提到的方法效果还不错。

关于PEP 307的内容:

__getstate__方法应该返回一个可以被序列化的值,这个值代表了对象的状态,但不能直接引用对象本身。

我的理解是,这意味着__getstate__方法必须返回一个不指向(无法被序列化的)原始对象的表示。因此,返回一个引用了自身的对象是可以的,只要不引用原始的(无法被序列化的)对象就行。

撰写回答