这是一个类实例的pickle,在pickle之后属性值被交换。
>>> class Foo(object):
... a = 1
... b = 2
... def __init__(self, c,d):
... self.c = c
... self.d = d
... def bar(self):
... return self.a,self.b,self.c,self.d
...
>>> f = Foo(3,4)
>>> _f = pickle.dumps(f)
>>> f.c,f.d = f.d,f.c
>>> f.b,f.a = f.a,f.b
>>> f_ = pickle.loads(_f)
>>> f_.bar()
(1, 2, 3, 4)
>>> f.bar()
(2, 1, 4, 3)
所以,这按预期工作。但是,python 通过引用来腌制类,因此如果您更改类定义,它似乎会更改实例属性。
>>> g = Foo(3,4)
>>> _g = pickle.dumps(g)
>>> g.c,g.d = g.d,g.c
>>> Foo.a,Foo.b = Foo.b,Foo.a
>>> g_ = pickle.loads(_g)
>>> g_.bar()
(2, 1, 3, 4)
>>> g.bar()
(2, 1, 4, 3)
如果你有一个列表或其他序列作为类属性,这一点会更加明显。所以,让我们在类中添加一些列表对象。
>>> Foo.a = []
>>> Foo.zap = lambda self:self.a
>>> Foo.baz = lambda self,x:self.a.append(x)
>>>
>>> h = Foo(3,4)
>>> h.baz(0)
>>> h.baz(1)
>>> h.zap()
[0, 1]
>>> _h = pickle.dumps(h)
>>> h.baz(2)
>>> h.baz(3)
>>> h_ = pickle.loads(_h)
>>> h_.zap()
[0, 1, 2, 3]
这是因为列表的值实际上存储在类属性而不是实例属性上。这是此类行为的众多示例之一。
如果您想“按原样”保留实例(即不通过引用存储),那么您应该使用dill 腌制。不过,dill 的泡菜会更大。
>>> import dill
>>> _h = dill.dumps(h)
>>> h.baz(4)
>>> h.baz(5)
>>> h_ = dill.loads(_h)
>>> h_.zap()
[0, 1, 2, 3]
>>> h.zap()
[0, 1, 2, 3, 4, 5]