Search code examples
pythondictionarypython-class

Problems with version control for dictionaries inside a python class


I'm doing something wrong in the code below. I have a method (update_dictonary) that changes a value or values in a dictionary based on what is specificed in a tuple (new_points).

Before I update the dictionary, I want to save that version in a list (history) in order to be able to access previous versions. However, my attempt below updates all dictionaries in history to be like the latest version.

I can't figure out what I'm doing wrong here.

test_dict = {'var0':{'var1':{'cond1':1,
                            'cond2':2,
                            'cond3':3}
                    }
            }

class version_control:
    
    def __init__ (self, dictionary):
        self.po = dictionary
        self.history = list()
        self.version = 0

    def update_dictionary(self, var0, var1, new_points):
            po_ = self.po
            self.history.append(po_)

            for i in new_points:
                 self.po[var0][var1][i[0]] = i[1]

            self.version += 1
    
    def get_history(self, ver):
        return self.history[ver]


a = version_control(test_dict)

new_points = [('cond1', 2),
             ('cond2', 0)]

a.update_dictionary('var0', 'var1', new_points)

new_points = [('cond3', -99),
             ('cond2', 1)]

a.update_dictionary('var0', 'var1', new_points)

print(a.get_history(0))
print(a.get_history(1))

Solution

  • Try this

    from copy import deepcopy
    
    ...
    def update_dictionary(self, var0, var1, new_points):
        po_ = deepcopy(self.po)
        self.history.append(po_)
    
        for i in new_points:
            self.po[var0][var1][i[0]] = i[1]
    
        self.version += 1
    ...
    

    The problem here is that when you assign po_= self.po you expect po_ to a new variable with a new memory id but actually, you just make a shallow copy(same memory id) of your dictionary. This means if you update the self.po then op_ will automatically update.


    To solve this problem by using deepcopy from the copy module(Built-in). It will create a new variable.


    You can use this code to save the data into a JSON file.

    import json
    
    class version_control:
    
        def __init__(self, dictionary):
            self.po = dictionary
            self.version = 0
    
            self.ZEROth_version()
    
        def update_dictionary(self, var0, var1, new_points, version=None):
            self.version += 1
    
            for i in new_points:
                self.po[var0][var1][i[0]] = i[1]
    
            # set the ver to version if given else set to self.version
            ver = self.version if version is None else version
    
            with open("version.json", "r") as jsonFile:
                # loading the data from the file. 
                data = json.load(jsonFile)
    
                data[str(ver)] = self.po
    
            with open("version.json", "w") as jsonFile:
                # save the updated dictionary in json file
                json.dump(data, jsonFile, indent=4)
    
        def get_history(self, ver):
            try:
                with open("version.json", "r") as jsonFile:
                    # I don't use .get here. I will catch key errors in except block. I don't want to add an if statement to check for None.  But string if you want you can add that.
                    return json.load(jsonFile)[str(ver)]
            # Checking if the file not exists or is empty
            except (json.decoder.JSONDecodeError, FileNotFoundError, KeyError) as e:
                print("File or Version not found")
    
        def ZEROth_version(self):
    
            with open("version.json", "w") as f:
                data = {0: self.po}
                json.dump(data, f, indent=4)
    
    

    I have explained some main points if you want more explanation then comment, I will reply as soon as possible.