AmericanMade AmericanMade - 4 months ago 10
JSON Question

__dict__ Doesn't Allow Call to Method in Python

I am creating a dictionary of objects that I'd like to save and re-load at a different time (I want to do this with the "json" module, not "pickle").

I have one class created (let's call it "Ball"), and the dictionary just contains many different instances of that class. The way it looks is:

my_dict = {
"ball1": {"size": "big", "baseball": False, etc...},
"ball2": {"size": "small", "baseball": True, etc...},
etc...
}


As I create new instances of the "Ball" class, I just APPEND them to "my_dict".

My "Ball" class has methods that allow me to change the value of certain fields...ie:

changeSize(self, size)


PROBLEM:

In order for me to get the save ability to work properly with the json module I have had to do the following to each new instance of the "Ball" class:

newBall = Ball(name)
my_dict[name] = newBall.__dict__


Adding the

.__dict__


and then appending that to the dictionary makes it JSON serializable, BUT it makes it so that when I go in to "Edit Mode" I can't call the method (ie changeSize(name) ) because it is just a dictionary and no longer a "Ball" object.

How do I make it so that I can save (using json module) and also use the methods that I have in place to edit?

Also, the way I am saving/loading is as follows:

out_file = open("testSave.json"), "w")
json.dump(my_dict, out_file, indent=4)
out_file.close()

in_file = open("testSave.json", "r")
my_dict = json.load(in_file)
in_file.close()


Thanks!

Answer

Ultimately, json doesn't support serializing arbitrary python objects. If you want to do that, you can have a look at pickle.

Alternatively, you can create an alternate constructor on your Ball that will initialize it with values from the dict:

class Ball(object):

    @classmethod
    def from_json(self, dictionary):
        b = cls()
        b.__dict__.update(dictionary)
        return b

    ...

I've written this assuming that the Ball constructor can be called with 0 arguments -- you may need to modify the code if that isn't the case, or if __init__ does anything "fancy" (beyond setting json serializable attributes). One way around the requirement of the arguments of __init__ is to use __new__ to create the instance and then just populate the members by updating the class dictionary:

class Ball(object):
    def __init__(self, name, foo, bar):
        self.name = name
        self.foo = foo
        self.bar = bar

    def to_dict(self):
        return self.__dict__

    @classmethod
    def from_dict(cls, dictionary):
        self = cls.__new__(cls)
        self.__dict__.update(dictionary)
        return self

    def __str__(self):
        return 'Ball(%r, %r, %r)' % (self.name, self.foo, self.bar)


b = Ball('HockyPuck', 'flat', 'NotABall')
d = b.to_dict()
bb = Ball.from_dict(d)

print(bb)

This works in both python2.x and 3.x.

Comments