Pickling and unpickling inherited attributes
Steve Holden
steve at holdenweb.com
Sun Oct 30 21:03:39 EST 2005
Alex wrote:
> I have a serious problem and I hope there is some solution. It is
> easier to illustrate with a simple code:
>
>
>>>>class Parent(object):
>
> __slots__=['A', 'B']
> def __init__(self, a, b):
> self.A=a; self.B=b
> def __getstate__(self):
> return self.A, self.B
> def __setstate__(self, tup):
> self.A, self.B=tup
>
>
>
>>>>class Child(Parent):
>
> __slots__=['C',]
> def __init__(self, c):
> self.C=c
> def __getstate__(self):
> return self.C,
> def __setstate__(self, tup):
> self.C, =tup
>
>
>
>>>>obj=Child(1)
>>>>obj.A=2
>>>>obj.B=3
>>>>obj.A
>
> 2
>
>>>>obj.B
>
> 3
>
>>>>obj.C
>
> 1
>
>>>>objct.Z=4
>
>
> Traceback (most recent call last):
> File "<pyshell#60>", line 1, in -toplevel-
> objct.Z=4
> AttributeError: 'Child' object has no attribute 'Z'
>
> So far so good.. Object obj inherited attributes (A and B) from the
> parent class and refuses to take any new ones. But look what happens
> when I try to pickle and unpickle it:
>
>
>>>>import cPickle
>>>>File=open('test', 'w')
>>>>cPickle.dump(obj, File)
>>>>File.close()
>>>>
>>>>file1=open('test', 'r')
>>>>objct=cPickle.load(file1)
>>>>file1.close()
>>>>objct.A
>
>
> Traceback (most recent call last):
> File "<pyshell#55>", line 1, in -toplevel-
> objct.A
> AttributeError: A
>
>>>>objct.C
>
> 1
>
> Its own attribute (C) value is restored but the value of an inherited
> attribute (A) is not. I tried pickling protocol 2, and module pickle
> instead of cPickle, all with the same result.
>
> What can be done?! Or maybe nothing can be done?
>
> I would greatly appreciate an advice. A lot of code is written, but now
> we have a need for pickling and unpickling objects and discovered this
> problem.
>
You have explicitly told the objects' class definition to only store the
C attribute as a part of the object state.
If you change the definition of child to:
class Child(Parent):
__slots__=['C',]
def __init__(self, c):
self.C=c
def __getstate__(self):
return self.A, self.B, self.C,
def __setstate__(self, tup):
self.A, self.B, self.C, =tup
everything works as you expect. But I presume you want the subclass to
require no knowledge of the superclass state?
In that case you might consider rewriting Child as
class Child(Parent):
__slots__ = ['C'] # only tuples need trailing comma
def __init__(self, c):
self.C = c
def __getstate__(self):
return Parent.__getstate__(self) + (self.C, )
def __setstate__(self, t):
self.C = t[-1]
Parent.__setstate__(self, t[:-1])
This would work adequately. May I ask your use case for __slots__? I
presume there is a specific reason why you can't just code the classes as
class Parent(object):
def __init__(self, a, b):
self.A = a
self.B = b
class Child(Parent):
def __init__(self, c):
self.C = c
since these definitions will pickle and unpickle perfectly.
A couple of other notes:
First, it's always safest to open pickle files in binary mode, as this
will eliminate the chance of platform differences making a pickle
written on one architecture or platform being unreadable by another.
Secondly, it's usual to have the subclass explicitly call the superclass
to initialize that portion of the instance. So it would be more
typically Pythonic to write
class Child(Parent):
def __init__(self, c):
Parent.__init__(self, a, b)
self.C = c
This allows you to pass the a and b values into the creator call in the
following way:
obj = Child(2, 3, 1)
rather than explicitly setting attributes of the Child instance in line.
If your inheritance patterns are complex you may find it useful to
investigate the super() function.
regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/
More information about the Python-list
mailing list