Pickle所有属性,除了一个

时间:2011-07-09 14:22:38

标签: python pickle

编写__getstate__方法的最佳方法是几乎所有对象的属性,但不包括一些?

我有一个包含许多属性的对象,包括一个引用instancemethod的属性。 instancemethod不是pickleable,所以当我试图挑选这个对象时我收到错误:

class Foo(object):
    def __init__(self):
        self.a = 'spam'
        self.b = 'eggs'
        self.c = 42
        self.fn = self.my_func
    def my_func(self):
        print 'My hovercraft is full of eels'

import pickle
pickle.dumps(Foo())              # throws a "can't pickle instancemethod objects" TypeError

这个__getstate__方法解决了这个问题,但后来我必须手动包含我要序列化的所有属性:

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

如果我有一个具有许多属性或频繁更改的对象,那么它的可扩展性或可维护性都不高。

我能想到的唯一选择是某种辅助函数,它迭代对象的属性,并根据类型将它们(或不是)添加到字典中。

6 个答案:

答案 0 :(得分:11)

  

我能想到的唯一选择是某种辅助函数,它迭代对象的属性,并根据类型将它们(或不是)添加到字典中。

是的,我认为这就是你剩下的,如果你想要足够的“魔法”让自己变得懒惰(和/或允许动态添加属性)。请记住,“pickle无法处理此问题”并不是您可能不希望在腌制状态下包含某些内容的唯一原因。

但这并不像你想象的那么难,假设你有“我应该腌制这个吗?”的代码。逻辑:

def __getstate__(self):
  return dict((k, v) for (k, v) in self.__dict__.iteritems() if should_pickle(v))

答案 1 :(得分:5)

使用之前答案中的is_instance_method

def __getstate__(self):
    return dict((k, v) for k, v in self.__dict__.iteritems()
                       if not is_instance_method(getattr(self, k)))

虽然is_instance_method操作也可以通过采用已知的实例方法“神奇地”执行,例如my_func,并采用其类型。

def __getstate__(self):
    instancemethod = type(self.my_func)
    return dict((k, v) for k, v in self.__dict__.iteritems()
                       if not isinstance(getattr(self, k), instancemethod))

答案 2 :(得分:3)

您可以随时删除不良内容:

def __getstate__(self):
    state = self.__dict__
    del state[...]
    return state

答案 3 :(得分:1)

我切入问题的根源,并尝试首先序列化所谓的“不可拾取”项目。 为此,我使用dill,它可以序列化python中的几乎任何东西。 Dill还有some good tools帮助您了解在代码失败时导致酸洗失败的原因。

>>> import dill
>>> dill.loads(dill.dumps(your_bad_object))
>>> ...
>>> # if you get a pickling error, use dill's tools to figure out a workaround
>>> dill.detect.badobjects(your_bad_object, depth=0)
>>> dill.detect.badobjects(your_bad_object, depth=1)
>>> ...

如果你绝对想要,可以使用dill的badobjects(或其他检测函数之一)递归地潜入对象的引用链,然后弹出不可触摸的对象,而不是每次调用它深度,如上所述。

答案 4 :(得分:0)

__slots__解决方案

如果您使用的是插槽,则可以避免重复使用以下内容排除成员:

class C(object):
    _pickle_slots = ['i']
    __slots__ = _pickle_slots + ['j']
    def __init__(self, i, j):
        self.i = i
        self.j = j
    def __getstate__(self):
        return (None, {k:getattr(self, k) for k in C._pickle_slots })

o = pickle.loads(pickle.dumps(C(1, 2), -1))

# i is there
assert o.i == 1

# j was excluded
try:
    o.j
except:
    pass
else:
    raise

在Python 2.7.6中测试。

答案 5 :(得分:0)

对于您的特定情况(防止函数被腌制),请使用以下命令:

self.__class__.fn = self.__class__.my_func

现在,您没有将函数添加到类的实例中,而是将其添加到了类本身中,因此该函数不会被腌制。如果您希望每个实例都有自己的fn版本,则此方法将无效。

我的场景是我想选择性地将get_absolute_url添加到某些Django模型中,并且希望在抽象的BaseModel类中进行定义。我有self.get_absolute_url = …,遇到了pickle问题。只需将__class__添加到作业中就可以解决我的情况。