Search code examples
pythonclassserializationstaticclass-variables

Serializable Static class variables in Python


Is it possible to have serializable static class variables or methods in python? As an example suppose, I have the following code snippet:

import pickle
class Sample:
    count = 0 # class variable
    def __init__(self, a1=0, a2=0):
        self.a = a1
        self.b = a2
        Sample.count += 1

#MAIN
f = open("t1.dat", "wb")
d = dict()
for i in range(10):
    s = Sample(i, i*i)
    d[i] = s
pickle.dump(d,f)
print "Sample.count = " + str(Sample.count)
f.close()

The output is:

Sample.count = 10

Now, I have another reader program similar to above:

import pickle
class Sample:
    count = 0 # class variable
    def __init__(self, a1=0, a2=0):
        self.a = a1
        self.b = a2
        Sample.count += 1

#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
print "Sample.count = " + str(Sample.count)

The output is:

Sample.count = 0

My question is: How do I load the class variable from my file? In other words, how do I serialize a class variable? If directly not possible, is there any alternative? Please suggest. Since class variable cannot be picked, as an alternative, I have used the code snippet in main part when reading from the file as below:

#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
Sample.count = len(d.values())
print "Sample.count = " + str(Sample.count)

The output is now:

Sample.count = 10

Is it acceptable solution? Any other alternative?


Solution

  • Quoting the section on "What can be pickled and unpickled?"

    Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply. Note that none of the class’s code or data is pickled, so in the following example the class attribute attr is not restored in the unpickling environment:

    class Foo:
        attr = 'a class attr'
    
    picklestring = pickle.dumps(Foo)
    

    So because attr, or in your case count, is part of the class definition, it never gets pickled. In your 'write' example, you're printing Sample.count which does exist but is not pickled in the first place.

    You could store Sample.count in each instance as _count and put Sample.count = self._count. But remember that since your d is a dict, they may unpickle in any order. So essentially this won't work.

    You'll need to add __setstate__ to your class customize the way it pickles and put in some flag value (like _count) which you then manipulate (via whatever logic works consistently) in __getstate__. (Edit: doesn't help with the given problem unless you store count in a global variable and access that in getstate and manipulate further each time an object is unpickled.)

    Another potential workaround but yuck: Add a variable to your dict d so that it also gets pickled. When you read it back, restore with Sample.count = d['_count']. So before pickle.dump(d,f) when you pickle, do d['_count'] = Sample.count.

    Important caveat: This is not actually allowing you to pickle Sample.count since what you're actually pickling (d) is a dictionary of Samples.

    Edit: The Sample.count = len(d.values()) which you've put as a workaround is very specific to your use case and not to class attr's in general.