cpickle and classes

Jonathan Hogg jonathan at onegoodidea.com
Tue Jul 2 09:08:04 EDT 2002


On 2/7/2002 12:30, in article
c07d0edc.0207020330.5f0d9ec5 at posting.google.com, "Guy" <guy at lightwork.co.uk>
wrote:

> If you look at the file there is NO sign of the
> Data002.flourUsage.ammount
> and Data002.flourUsage.date values or var names.
> 
> Why ? 
> Can cpickle not handle this ?

Aha. This is because you have mixed up class and instance variables. Looking
at the example:

> class mill:
>   time = ""
>   date = ""
>   name = ""
>   flourUsage = flour()
>   add =[""]
> 
> class flour:
>   ammount = ""
>   date = ""

The variables 'time', 'date', 'name', 'flourUsage', 'add', 'ammount' and
'date' are all class variables - that is they are initialised within the
class, not when instantiating a new instance - and thus "belong" to the
class not instances of it.

> data = mill()
> 
> data.time = "1"
> data.date = "2"
> data.name = "3"
> data.flourUsage.ammount = "4"
> data.flourUsage.data = "5"
> data.add=["adsfads","dsgdsfgdf"]

Here you instantiate a 'mill' object, 'data', which has a pointer to its
class and thus to the class variables. However when you do an attribute
assignment, like 'data.time = "1"', you create a new instance variable that
hides the class one.

However, 'data.flourUsage.ammount = "4"' looks up the *class* variable
'flourUsage' and then overrides its 'ammount' variable with "4".

Now, to answer your original question, pickle does not write out the class
variables since they are not part of the instance you are pickling. So the
variables you overrode, 'time', 'date' and 'name', will be written out, but
not 'flourUsage'.

I think the main thing going wrong is that you have misunderstood how to use
classes. The variables shouldn't be assigned in the class body, they should
be assigned in an '__init__' method, something like this:

>>> class mill:
...     def __init__( self ):
...         self.time = ""
...         self.date = ""
...         self.name = ""
...         self.flourUsage = flour()
...         self.add = [""]
... 
>>> class flour:
...     def __init__( self ):
...         self.amount = ""
...         self.date = ""
... 
>>> data = mill()
>>> data.time
''
>>> data.time = "1"
>>> data.flourUsage.amount = "4"

Now all of these variables are instance variables, not class variables:

>>> mill.time
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: type object 'mill' has no attribute 'time'
>>> 

This is at it should be, the 'mill' class itself does not have a time, only
instances of it should have a time.

Now if we pickle 'data' we get something like:

>>> import cPickle
>>> 
>>> cPickle.dumps( data )
"ccopy_reg\n_reconstructor\np1\n(c__main__\nmill\np2\nc__builtin__\nobject\n
p3\nNtRp4\n(dp5\nS'date'\np6\nS''\nsS'add'\np7\n(lp8\nS''\nasS'flourUsage'\n
p9\ng1\n(c__main__\nflour\np10\ng3\nNtRp11\n(dp12\ng6\nS''\nsS'amount'\np13\
nS'4'\nsbsS'name'\np14\nS''\nsS'time'\np15\nS'1'\nsb."
>>>   

which you can see now includes the 'flourUsage' object, the values we have
set, and all the empty initial values.

> Is there anything I can do to improve the code to make cpickle work ?
> Are there any work arounds for this ?
> Maybe suggestions of how to do this in a different way.

Well hopefully this has helped to clear it up. In summary:

* Definitions made within the 'class' construct become attributes of the
  class itself. Usually these are just the methods, which are shared
  between all instances of the class.

* Attribute assignments made to 'self' in an '__init__' method create
  instance variables. These are unique to each new instance and are not
  shared.

* Pickle (and cPickle) only saves and restores instance variables since
  these represent the unique information about that instance.

Jonathan




More information about the Python-list mailing list