Dict instance variable being changed for all instances

Question:

I made a simple repro of my issue below. I have a class with an instance variable (inst_dict) which is of type dict. I also have a class variable (myClass_dict) which I use to keep track of each instance of my class.

What I am trying to do is update one value of inst_dict with a certain value. My problem is that it is updating the value for every instance.

Below is the code that almost does what I am aiming to do. The line inst.inst_dict = {"key1": i, "key2": "Val2"} sets the whole dict to the new value based on i. I am trying to just set one value in the dict, rather than the whole dict. The line after that is commented out is what I expected to work, where I access inst_dict for the current instance, then set the value of "key" to i.

When I run it with that line it updates the value of "key" in the dict, but for every instance. It’s like it gets changed to a class variable somewhere.

class My_Class(object):
    myClass_dict = {}

    def __init__(self, name,  inst_dict = {}):
        My_Class.myClass_dict[name] = self
        self.name = name
        self.inst_dict = inst_dict

for i in range(5):
    name = f'inst{i}'
    inst = My_Class(name)

    inst.inst_dict = {"key1": i, "key2": "Val2"}    #This works but changes the whole dict
    # inst.inst_dict['key'] = i   #This changes one value, but updates every instance
    print(name)

    for inst in My_Class.myClass_dict:
        print(f' {My_Class.myClass_dict[inst].name} {My_Class.myClass_dict[inst].inst_dict}')

Can somebody explain why this is updating every instance, and how I can avoid that?

Thanks!

edit:
Here is the output when running this with the first line (as written above):

inst0
 inst0 {'key1': 0, 'key2': 'Val2'}       
inst1
 inst0 {'key1': 0, 'key2': 'Val2'}       
 inst1 {'key1': 1, 'key2': 'Val2'}       
inst2
 inst0 {'key1': 0, 'key2': 'Val2'}       
 inst1 {'key1': 1, 'key2': 'Val2'}       
 inst2 {'key1': 2, 'key2': 'Val2'}       
inst3
 inst0 {'key1': 0, 'key2': 'Val2'}       
 inst1 {'key1': 1, 'key2': 'Val2'}       
 inst2 {'key1': 2, 'key2': 'Val2'}       
 inst3 {'key1': 3, 'key2': 'Val2'}       
inst4
 inst0 {'key1': 0, 'key2': 'Val2'}       
 inst1 {'key1': 1, 'key2': 'Val2'}       
 inst2 {'key1': 2, 'key2': 'Val2'}       
 inst3 {'key1': 3, 'key2': 'Val2'}       
 inst4 {'key1': 4, 'key2': 'Val2'}

As you can see, at each loop it updates the corresponding instance key1 to the value of i (0,1,2,3,4). And the value of key1 for inst0 remains unchanged in the following loops.

And here is when I comment that line, and uncomment the next line:

inst0
 inst0 {'key': 0}
inst1
 inst0 {'key': 1}
 inst1 {'key': 1}
inst2
 inst0 {'key': 2}
 inst1 {'key': 2}
 inst2 {'key': 2}
inst3
 inst0 {'key': 3}
 inst1 {'key': 3}
 inst2 {'key': 3}
 inst3 {'key': 3}
inst4
 inst0 {'key': 4}
 inst1 {'key': 4}
 inst2 {'key': 4}
 inst3 {'key': 4}
 inst4 {'key': 4}

As you can see, at each loop, it updates every instance that has been created. On the fourth loop, it not only updates inst4, but also inst 0 through 3.

Asked By: Locks

||

Answers:

Long story short: dict in python is mutable and therefore not advisible to be used as default value. See this post for example: Why is the empty dictionary a dangerous default value in Python?

The empty dict is constructed only once when defining the class and then every instance uses it – and updates it. EXCEPT for when you assign a whole new value: That works. If you want to have a "fresh" empty dict for every instance, the common construction is:

class My_Class(object):
    myClass_dict = {}

    def __init__(self, name,  inst_dict = None):
        My_Class.myClass_dict[name] = self
        self.name = name
        if inst_dict is not None:
            self.inst_dict = inst_dict
        else:
            self.inst_dict = {}
Answered By: miriess