如何序列化一个继承自A并定义__setstate__和__getstate__的B类对象?

5 投票
2 回答
4511 浏览
提问于 2025-04-17 08:42

我的问题是:

class A(object):
    def __init__(self):
        #init
    def __setstate__(self,state):
        #A __setstate__ code here            
    def __getstate__(self):
        #A __getstate__ code here
        return state

class B(A):
    def __init__(self):
        #creates many object variables here

A 是一个外部库里的东西。

困难的解决方案

我想尽量避免这样做。

当对 B 进行序列化(也就是把对象转换成可以存储或传输的格式)时,序列化工具会使用 A 类的 __setstate____getstate__ 方法。所以为了让序列化正常工作,我应该做一些这样的事情:

class B(A):
    def __init__(self):
        #creates many object variables here

    def __setstate__(self,state)
        A.__setstate__(self,state)
        #B __setstate__ code here
        #getting various variables from state for example
        self._a0 = state['a0']
        self._a1 = state['a1']
        #...
        self._a100 = state['a100']
        self._a101 = state['a101']

    def __getstate__(self):
        state = A.__getstate__(self)
        #B __setstate__ code here
        #filling state with various variables  
        #getting various variables from state for example
        state['a0'] =  self._a0
        state['a1'] =  self._a1
        #...
        state['a100'] =  self._a100
        state['a101'] =  self._a101           
        return state

我的问题是:

我怎么才能不在 B 中定义 __setstate____getstate__,让序列化工具自己处理变量的序列化?B 中的所有变量都是可以被序列化工具处理的类型。所以如果 B 不继承 A,那就能很顺利地做到:

b = B()
path = 'path.temp'
fout = open(path,'w')
pickler = pickl.Pickler(fout)

pickler.dump(b)
fout.close()

fin = open(path,'r')
upickler = pickl.Unpickler(fin)
b = unpickler.load()
fin.close()
#b has all variables

明显的解决方案

class B(object):
    def __init__(self):
        #creates many object variables here
        a = A()            

不过我希望 B 能继承 A。有没有什么办法解决这个问题,或者至少让 B 中的变量的序列化和反序列化自动化?

变通的解决方案:

关于在 困难的解决方案 中自动化序列化

在 B 中添加一个字典,用来存放需要序列化的变量:

class B(A):
    __picklableObjects__ = {'_a0', '_a1', ... ,'_a101'}

    def __init__(self):
        #creates many object variables here
        A.__init__(self)
        self._a0 = ...
        ...
        self._a101 = ...

    @staticmethod
    def getPicklableObjects():
        return B.__picklableObjects__

    def __setstate__(self,state):
        A.__setstate__(self,state)
        for po in B.getPicklableObjects():
           __dict__[po] = state[po]

    def __getstate__(self):
        state = A.__getstate__(self)
        for po in B.getPicklableObjects():
            state[po] = copy.deepcopy(__dict__[po])
        return state

还有其他想法吗?

A 的库:

好吧,对于感兴趣的朋友,A 是 graph_tool.Graph: A 的源代码

line 786: class Graph(object)

...

line 1517: __getstate__

...

line 1533: __setstate__

2 个回答

0

Pickle的默认行为是,如果没有定义__getstate__,它会把对象的__dict__属性里的内容进行序列化。简单来说,__dict__就是存放实例属性的地方。

所以在你的情况中,你只需要让A的获取和设置状态的方法,能够保存self.__dict__里的值,并在__setstate__时恢复这些值。这样做可以保留所有A的子类实例的变量。

4

根据文档的说明,当没有定义 __getstate__ 这个方法时,实例的 __dict__ 会被序列化(也就是保存成一种可以存储或传输的格式)。所以,也许你可以利用这个特性,结合 A 类的方法和实例的 __dict__,来定义你自己的状态方法:

import pickle

class A(object):
    def __init__(self):
        self.a = 'A state'

    def __getstate__(self):
        return {'a': self.a}

    def __setstate__(self, state):
        self.a = state['a']

class B(A):
    def __init__(self):
        A.__init__(self)
        self.b = 'B state'

    def __getstate__(self):
        a_state = A.__getstate__(self)
        b_state = self.__dict__
        return (a_state, b_state)

    def __setstate__(self, state):
        a_state, b_state = state
        self.__dict__ = b_state
        A.__setstate__(self, a_state)

b = pickle.loads(pickle.dumps(B()))
print b.a
print b.b

撰写回答