-1

I have a python class which has been serialized in JSON.

import json

class Data:
    def __init__(self):
        self.ival = 45
        self.fval = 0.5
        self.str = 'A string'
       
    def save(self, filename):
        with open(filename, 'w') as dfile:
            jstr = json.dumps(self.__dict__)
            dfile.write(jstr)
    
    def load(self, filename):
        with open(filename, 'r') as dfile:
            jstr = dfile.read()
            jdict = json.loads(jstr)
            # Save what is currently there
            jsave = self.__dict__
            # Restore the old stuff
            self.__dict__ = jdict
            # Restore the original stuff
            for member in jsave.keys():
                if not member in jdict:
                    self.__dict__[member] = jsave[member]
    
    def scramble(self):
        self.ival = 99
        self.fval = 3.162
        self.str = 'another'

dfilename = './data.json'
dobj = Data()
dobj.save(dfilename)

Some time later, I add another value to the class

class Data:
    def __init__(self):
        ...
        self.itwo = 2

...
dfilename = './data.json'
dobj = Data()
dobj.load(dfilename)
print(dobj.itwo)

This appears to work.

The question is, is this the best way of restoring a dictionary? I wish to restore the values that were there but keep the ones that weren't there. Is there a better way of merging dictionaries other than going through one key at a time as I have done?

CoderTang
  • 460
  • 5
  • 14
cup
  • 7,589
  • 4
  • 19
  • 42
  • 2
    Are you looking for `dict.update`? – Mad Physicist Sep 06 '21 at 00:16
  • 1
    Yes - dict.update is exactly what I'm looking for. Really ought to read the docs. Replaced the code with **self.__dict__.update(jdict)** and it works. If you wish to write it up as an answer, I'll accept it. – cup Sep 06 '21 at 06:45

3 Answers3

1

If you want to clobber the existing values in memory with what's in the file:

self.__dict__.update(jdict)

If you want to go the other way, there are a couple of simple options. Both start with

jdict.update(self.__dict__)

You can then swap references

self.__dict__ = jdict

Or copy values back:

self.__dict__.update(jdict)
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
0

This isn't the best way of course, but to achieve this with minimal efforts, you could use setdefault() method on a dict.

self.__dict__[member] = jsave[member]

->

self.__dict__.setdefault(member, jsave[member])

Regarding the better way: take a look at getstate and setstate or at some high-level tools like Pydantic or Dataclasses

madbird
  • 1,326
  • 7
  • 11
0

You can use dict.update or union |.

def load(self, filename):
    with open(filename, 'r') as dfile:
        jdict = json.load(dfile)
        jsave = self.__dict__
        self.__dict__ = jdict | jsave

        # self.__dict__ = jdict
        # self.__dict__.update(jsave)

Just a few other notes: Use json.load and json.dump when you can. for key in jsave is valid because you iterate over keys by default. Look into setattr as well.

Edit: As madbird pointed out in the comments, if you're using something before Python 3.9, you will not be able to use the union operator. dict.update has been around in Python 2, but if you don't want to use the temporary assignment, you can use {**jdict, **jsave} as suggested by madbird.

HelixAchaos
  • 131
  • 1
  • 3
  • Dicts union is not available in python versions prior to 3.8 iirc, so I could suggest to use in answers some older approach like `{**a, **b}` – madbird Sep 06 '21 at 00:00
  • Good point. I just looked it up, and it was [introduced in Python 3.9](https://www.python.org/dev/peps/pep-0584/). I should have considered the version. Thank you. – HelixAchaos Sep 06 '21 at 00:03
  • Unfortunately I'm using 3.7 but worth considering when I move to 3.9. – cup Sep 06 '21 at 00:10