2

I am trying to create an object which can serialize and deserialize itself with a class method, without using the return statement (I do not want a static method). This is a concept of my class and a naive use case:

import pickle

class A:
    def __init__(self):
        self.x = 0

    def add(self):
        self.x += 1

    def serialize(self, filename):
        """Simple object serialization given a filename"""
        with open(filename, 'wb') as f:
            pickle.dump(self, f)
            print("Serialized with value of: %d" % (self.x))

    def deserialize(self, filename):
        """Simple object deserialization given a filename"""
        with open(filename, 'rb') as f:
            print("self.x before load (inside deserialize): %d" % (self.x))
            self = pickle.load(f)
            print("Deserialized value inside deserialize function: %d" % (self.x))



a1 = A()
a2 = A()

a1.add()
a1.add()

a1.serialize('a.pkl')
a2.deserialize('a.pkl')

print("Deserialized value outside: %d" % (a2.x))

However, once I leave the deserialization method, self (in this case, the instance a2) does not keep its value.

Output when run:

>> Serialized with value of: 2
>> self.x before load (inside deserialize): 0
>> Deserialized value inside deserialize function: 2
>> Deserialized value outside: 0

Why is this happening? I have also tried with deepcopy just after the pickle.load in the function deserialize but nothing seems to work, and I would like to understand why.

Thanks in advance

Iván Sánchez
  • 248
  • 1
  • 10
  • You need a few more functions to make this work. Otherwise you're just dumping the memory of the class constructor, not the instance of said class (if i'm not mistaken): https://docs.python.org/3/library/pickle.html#pickling-class-instances. A good explanation of the difference between a instance variable and a class variable can be found here: https://stackoverflow.com/questions/10842553/pickle-with-custom-classes – Torxed Oct 17 '18 at 11:29
  • First of all please avoid `self = pickle.load(f)` and then in the `deserialize` method instead of outputting the read value, you are just asking it to print `self.x` which seems wrong to me. – jar Oct 17 '18 at 11:30
  • Possible duplicate of [Pickle with custom classes](https://stackoverflow.com/questions/10842553/pickle-with-custom-classes) – Torxed Oct 17 '18 at 11:34
  • @Torxed I have read it already, but his problem is that the empty variable is a class variable, not an instance variable. In my case, after the deserialization, the unchanged variable is an instance variable – Iván Sánchez Oct 17 '18 at 11:38
  • That's because you don't call the appropriate built in functions `__update__` etc. That is, if you really don't want a return function as mentioned in the solution below. – Torxed Oct 17 '18 at 11:40
  • @Torxed I cannot find anything related to `__update__`. Could you specify a reference, please? – Iván Sánchez Oct 17 '18 at 11:51

2 Answers2

2

The reason this doesn't work is that because you can't assign to self (or rather: doing that doesn't do what you think it does). If you're interested to find out what actually happens, try assigning something weird to self, e.g. self = "foobar" (the behaviour will be unchanged).


Make deserialize a classmethod and use it as a "constructor":

@classmethod
def deserialize(cls, filename):
    """Simple object deserialization given a filename"""
    with open(filename, 'rb') as f:
        obj = pickle.load(f)
        print("Deserialized value inside deserialize function: %d" % (obj.x))
        return obj

Then use it like this:

a2 = A.deserialize('a.pkl')

Output:

Serialized with value of: 2
Deserialized value inside deserialize function: 2
Deserialized value outside: 2

L3viathan
  • 26,748
  • 2
  • 58
  • 81
  • This is what I was trying to avoid. I did not know I can't assign to `self`. Any reason or documentation on why this is not possible? Thanks! – Iván Sánchez Oct 17 '18 at 11:40
  • 1
    Just found this: https://stackoverflow.com/questions/1216356/is-it-safe-to-replace-a-self-object-by-another-object-of-the-same-type-in-a-meth – Iván Sánchez Oct 17 '18 at 11:41
2

Sorry for the late response. Pickle flattens objects as dictionaries. This is how to fix it:

    def serialize(self, filename):
        """Simple object serialization given a filename"""
        with open(filename, 'wb') as f:
            pickle.dump(self.__dict__, f)
            print("Serialized with value of: %d" % (self.x))

    def deserialize(self, filename):
        """Simple object deserialization given a filename"""
        with open(filename, 'rb') as f:
            print("self.x before load (inside deserialize): %d" % (self.x))
            self.__dict__ = pickle.load(f)
            print("Deserialized value inside deserialize function: %d" % (self.x))