如何在不修改原始类的情况下移除实例方法对象以满足pickle要求

-1 投票
3 回答
681 浏览
提问于 2025-04-17 19:37

我想要持久保存一个来自 reverend.thomas.Bayes 的对象。当然,如果我直接尝试对这些类进行序列化(也就是“腌制”),我会遇到以下问题:

TypeError: can't pickle instancemethod objects

为了解决这个问题,我尝试声明了两个函数:

import types
from itertools import chain
from copy import copy
from reverend.thomas import Bayes

def prepare_bayes_for_pickle(bayes_obj):
    dic = copy(bayes_obj.__dict__) #I also tried using deepcopy instead of copy
    for k in dic:
        if type(k) == types.MethodType:
            dic.pop(k)
    return dic

def reconstruct_bayes_from_pickle(bayes_dic):
    b = Bayes()
    # Merge b with bayes_dic, with bayes_dic taking precedence
    dic = dict(chain(bayes_dic, b))
    b.__dict__ = dic
    return b

基本上,我试着复制这个对象的 __dict__,然后通过检查类型来移除 instancemethod,看看它是不是 types.MethodType

接着,我会通过创建一个新的 Bayes 对象来重建这个对象,然后把它和 bayes_dic 合并在一起(在它被反序列化之后)。

不过,我还没有进行到第二种方法,因为我仍然无法对从 prepare_bayes_for_pickle 返回的对象进行序列化,而不出现原来的错误。

3 个回答

0

这听起来就像是想把一个方形的木桩塞进一个圆形的洞。那不如试试用pickle来处理这些参数,把它们“腌制”起来,然后再用“解腌”的方法来重建reverand.Thomas.Bayes这个对象?

>>> from collections import namedtuple
>>> ArgList = namedtuple('your', 'arguments', 'for', 'the', 'reverand')
>>> def pickle_rtb(n):
...     return pickle.dumps(ArgList(*n.args))
... 
>>> def unpickle_rtb(s):
...     return reverand.Thomas.Bayes(*pickle.loads(s))
... 
>>> s = pickle_rtb(reverand.Thomas.Bayes(1, 2, 3, 4, 5)) # note arguments are a guess
>>> rtb = unpickle_norm(s)

这个想法是受到这个StackOverflow问题的启发。

0

k 是一个键,也就是属性或方法的名字。你需要测试这个属性本身:

    if type(dic[k]) == types.MethodType:
            ^~~~~~ here

我更喜欢用列表推导式;你也应该使用 isinstance 来检查:

dic = dict((k, v) for k, v in bayes_obj.__dict__
           if not isinstance(v, types.MethodType))
2

更好的解决办法是给Bayes类添加一个__getstate__方法,同时还要有一个__setstate__方法:

import types
from reverend.thomas import Bayes

def Bayes__getstate__(self):
    state = {}
    for attr, value in self.__dict__.iteritems():
        if not isinstance(value, types.MethodType):
            state[attr] = value
        elif attr == 'combiner' and value.__name__ == 'robinson':
            # by default, self.combiner is set to self.robinson
            state['combiner'] = None
    return state

def Bayes__setstate__(self, state):
    self.__dict__.update(state)
    # support the default combiner (an instance method):
    if 'combiner' in state and state['combiner'] is None:
        self.combiner = self.robinson

Bayes.__getstate__ = Bayes__getstate__
Bayes.__setstate__ = Bayes__setstate__

这样一来,Bayes类就可以随时被“打包”和“解包”,而不需要额外的处理。

我注意到这个类有一个self.cache = {}的映射;也许在打包的时候应该把它排除掉?在__getstate__中忽略它,如果是这样的话,在__setstate__中调用self.buildCache()

撰写回答