Pickling and unpickling inherited attributes

Steve Holden steve at holdenweb.com
Mon Oct 31 03:03:39 CET 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