如何覆盖pickle类中的dump / load方法 - 自定义pickle和unpickling - Python

时间:2012-08-03 18:28:34

标签: python pickle

到目前为止,我所做的就是:

import pickle

class MyPickler(pickle.Pickler):
    def __init__(self, file, protocol=None):
        super(MyPickler, self).__init__(file, protocol)

class MyUnpickler(pickle.Unpickler):
    def __init__(self, file):
        super(MyUnpickler, self).__init__(file) 

在我的主要方法中,这主要是我所拥有的

#created object, then... 
pickledObject = 'testing.pickle'
with open(pickledObject,'wb') as f:
    pickle = MyPickler(f)
    pickle.dump(object) #object is the object I want to pickle, created before this

with open(pickledObject, 'r') as pickledFile:
    unpickle = MyUnpickler(pickledFile)
    object2 = unpickle.load()

然而,当调用super方法时,这会给我以下错误: TypeError: must be type, not classobj

如何只覆盖两种方法,加载和转储? pickle文件位于C:\ Python27 / lib / pickle.py

修改 可以在此处找到enum.py文件:http://dpaste.com/780897/

对象详细信息: 对象初始化如下:

object = CellSizeRelation(CellSizeRelation.Values.FIRST)

CellSizeRelation是一个使用Enumeration的类:

class CellSizeRelation(Option):
    Values = enum.Enum('FIRST',
                       'SECOND')

在我挑选对象之前,我这样做:

print object.Values._values 
print object.value.enumtype 

输出

[EnumValue(<enum.Enum object at 0x02E80E50>, 0, 'FIRST'), EnumValue(<enum.Enum object at 0x02E80E50>, 1, 'SECOND')
<enum.Enum object at 0x02E80E50>

在我解开并打印出同样的东西之后,我得到了这个输出

[EnumValue(<enum.Enum object at 0x02E80E50>, 0, 'FIRST'), EnumValue(<enum.Enum object at 0x02E80E50>, 1, 'SECOND')
<enum.Enum object at 0x02ECF750>

问题是第二个对象地址发生了变化;第一次初始化时,enumtype_values具有相同的地址。但是,在拆除后,它们会更改地址。当我尝试比较两个enumValues时,这会破坏我的代码。如果你查看enumValue类,比较函数会尝试这样做:

try:
        assert self.enumtype == other.enumtype
        result = cmp(self.index, other.index)

由于地址更改,断言功能失败。 我现在以某种方式需要确保enumtype的地址在unpickled时不会改变。我想只是从unpickled文件中获取值'FIRST',找到它的索引,然后重新初始化对象:

def load:
    object = CellSizeRelation(CellSizeRelation.Values[INDEX])
    return object

2 个答案:

答案 0 :(得分:7)

您希望自定义对象状态被pickle和unpickled的方式,而不是自定义加载和卸载功能。

您必须研究Pickling and unpickling normal class instances chapter,在您的情况下,定义__getstate____setstate__方法就足够了。

在您的情况下,会发生一个具有EnumValue个实例的类级别属性,这些属性应该是常量。但是在unpickling上,创建了新的EnumValue实例,它们不再连接到类级属性。

EnumValue个实例确实有一个index属性可用于将其状态捕获为整数而不是EnumValue的实例,我们可以使用它再次找到正确的常量恢复实例时:

 class CellSizeRelation(Option):
     # skipping your enum definition and __init__ here

     def __getstate__(self):
         # capture what is normally pickled
         state = self.__dict__.copy()
         # replace the `value` key (now an EnumValue instance), with it's index:
         state['value'] = state['value'].index
         # what we return here will be stored in the pickle
         return state

     def __setstate__(self, newstate):
         # re-create the EnumState instance based on the stored index
         newstate['value'] = self.Values[newstate['value']]
         # re-instate our __dict__ state from the pickled state
         self.__dict__.update(newstate)

因此,通常情况下,如果没有__getstate__实例__dict__被腌制。我们现在确实返回了__dict__的副本,但是我们将EnumValue实例替换为它的索引(一个简单的整数)。在unpickling上,通常新实例__dict__会使用我们在pickle上捕获的未标记__dict__进行更新,但现在我们已经定义了__setstate__,我们可以将枚举索引换出来再次正确EnumValue

答案 1 :(得分:0)

EnumValue取决于id“枚举类型”对象之间的Enum标识。这有一些优点和缺点。

主要优点是对Enum('A', 'B')的两次调用定义了不同的枚举类型。所以:

osx = Enum('Jaguar', 'Tiger', 'Leopard')
bigcats = Enum('Jaguar', 'Tiger', 'Leopard')

如果您希望能够将OS X 10.4与条纹查杀机区分开来,这可能很有用。

但这也意味着当pickle unpickles osxbigcats时,它们不仅会彼此不同,它们也将与{}的任何早期实例区别开来。 {1}}和osx。一旦你想到它,现在真的有了。

所以,你的解决方案不能涉及任何类型的黑客泡菜;它将不得不涉及黑客攻击bigcats模块。

您需要为enum定义合理的__cmp__方法,以便对您有所帮助。如果你可以放弃osx-than-bigcats的区别,那很容易。如果你不能,你还需要一些其他的方法(可能会在Enum定义中添加一个显式标记名,或者是一个可选但在其他方面隐式自动递增计数器?)来处理它。