如何对一个继承自A类、具有多个变量的B类对象进行pickle,该类定义了__setstate__和__getstate__方法。

5

我的问题是:

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进行pickling时,pickle会使用A类的__setstate____getstate__方法,因此为了使pickle正常工作,我应该做如下处理:

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__,以便pickle自己完成变量的序列化工作? B中的所有变量都是pickle可以自行处理的类型。 因此,如果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中的变量进行pickle / unpickle吗?

解决方案:

针对在Hard Solution中自动化pickling的问题:

在B中添加一个包含要pickle的变量的字典:

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感兴趣的人,A是graph_tool.Graph: A src code

line 786: class Graph(object)

...

line 1517: __getstate__

...

line 1533: __setstate__

你有A的代码访问权限吗?我通常以某种“选择退出”的方式编写我的__getstate__方法,其中我复制实例的__dict__属性并删除或修改不可拾取的条目。 这将很好地传递给子类。 - David Zwicker
是的,我可以访问A。 A在外部库中。修改A代码可能会带来问题,因为我不确定它会如何影响该库的其余部分。换句话说,您会建议类似于“解决方法”中的某些东西吗? - user779686
你考虑过使用其他的序列化工具吗? - Karl Knechtel
жҲ‘жӣҙеҖҫеҗ‘дәҺдҝ®ж”№Aзҡ„д»Јз Ғд»ҘйҒҝе…Қз ҙеқҸ继жүҝе…ізі»гҖӮеҰӮжһңдёҖдёӘзұ»й»ҳи®Өд»Һе®ғзҡ„__getstate__ж–№жі•иҝ”еӣһжүҖжңүеҶ…容并仅жҺ’йҷӨжңүй—®йўҳзҡ„еұһжҖ§пјҢжҲ‘и§үеҫ—жӣҙеҠ ж–№дҫҝгҖӮеҰӮжһңжӮЁиғҪзӣёеә”ең°дҝ®ж”№AпјҢеҲҷе®Ңе…ЁдёҚйңҖиҰҒеңЁBдёӯзј–еҶҷд»»дҪ•д»Јз ҒгҖӮ - David Zwicker
我希望修改A的代码是最后的选择。因为A是一个外部的Python库。此外,我宁愿放弃从A继承而不是修改A。 - user779686
2个回答

4
根据文档,当未定义__getstate__时,实例的__dict__被pickle。因此,您可以使用此方法将自己的状态方法定义为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

好的,但是A定义了__setstate__,__getstate__以防止pickle一些东西(不知道它们是什么,也不知道为什么这样做——类非常复杂)。你在__getstate__中提出的解决方案会导致变量或至少是A的属性(它们在a_state和b_state中)重复。我还想知道,用这个解决方案,是否应该仔细检查A的代码,以防止在b_state变量中pickle被A忽略的然后进行设置的潜在回馈。因为其中一些被A.__setstate__覆盖了,而其他则没有。 - user779686
@user779686 没错,b_state 中的一部分状态在 a_state 中是重复的。此外,在 B.__getstate__B.__setstate__ 中的排序优先考虑了 A 方法。然而,我认为这是适当的,以避免破坏 A 的行为。 - jcollado
我将尝试使用这个解决方案,并查看是否会对A产生一些负面影响,以及潜在地取消序列化可能不需要的变量/状态。 - user779686

0

Pickle 的默认行为是,如果未定义 __getstate__,则会将对象的 __dict__ 属性中存储的内容进行序列化 - 这就是实例属性存储的位置。

因此,在您的情况下,看起来您只需要使 A 的 get 和 set 状态保留在 self.__dict__ 中找到的值,并在 __setstate__ 中恢复它们 - 这应该可以保留 A 的所有子类实例变量。


更改A有很大的机会破坏依赖于A的其他任何东西。 - Anthon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接