编写__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 }
如果我有一个具有许多属性或频繁更改的对象,那么它的可扩展性或可维护性都不高。
我能想到的唯一选择是某种辅助函数,它迭代对象的属性,并根据类型将它们(或不是)添加到字典中。
答案 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__
添加到作业中就可以解决我的情况。