I recently had a need to read values from a file and insert them into a dictionary of dictionaries. This could be useful for having, say, dictionary with hostnames of some devices as keys and values being dictionaries with data collected from each of the devices.

To achieve that I created a helper dictionary which had data for each device that I read from a file. Then I would assign a copy of a helper dictionary to the key equal to the name of the device for which I collected data.

My code was functionally identical to the below snippet:

parent_dict = dict()
child_dict = dict()

for p_key in range(1,6):
    for c_key in range(1,6):
        child_dict[c_key] = p_key * c_key
    parent_dict[p_key] = child_dict

pprint(parent_dict)

And the result of running that code is:

{1: {1: 5, 2: 10, 3: 15, 4: 20, 5: 25},
 2: {1: 5, 2: 10, 3: 15, 4: 20, 5: 25},
 3: {1: 5, 2: 10, 3: 15, 4: 20, 5: 25},
 4: {1: 5, 2: 10, 3: 15, 4: 20, 5: 25},
 5: {1: 5, 2: 10, 3: 15, 4: 20, 5: 25}}

Wait, that doesn't look right. Well, as I found out, I wasn't copying anything. Instead I created 5 references to the same dictionary object and assigned them to 5 different keys. This is why I ended up with all 5 keys in the parent dictionary having the same value.

This code shows me that all of the keys refer to the same object id:

for k, v in parent_dict.iteritems():
    print hex(id(v))
0x2395158L
0x2395158L
0x2395158L
0x2395158L
0x2395158L

It turns out Python does not implicitly copy dictionaries. This is true for most of the objects in Python, not just dictionaries, and this is where explicit copy needs to be used.

In my case I had to use deepcopy method, which is required when object contains other objects, like dictionaries or lists. If the object doesn't contain other objects then copy method can be used.

The original code snippets rewritten to use deepcopy:

import copy

for p_key in range(1,6):
    for c_key in range(1,6):
        child_dict[c_key] = p_key * c_key
    parent_dict[p_key] = copy.deepcopy(child_dict)

pprint(parent_dict)

And the result:

{1: {1: 1, 2: 2, 3: 3, 4: 4, 5: 5},
 2: {1: 2, 2: 4, 3: 6, 4: 8, 5: 10},
 3: {1: 3, 2: 6, 3: 9, 4: 12, 5: 15},
 4: {1: 4, 2: 8, 3: 12, 4: 16, 5: 20},
 5: {1: 5, 2: 10, 3: 15, 4: 20, 5: 25}}

Much better, all values are as expected now.

Let's run our final check to confirm each key is referencing a unique id:

for k, v in parent_dict.iteritems():
    print hex(id(v))
0x2465268L
0x2465488L
0x2465598L
0x24656a8L
0x24657b8L

As we can see, all of the ids are unique just as we wanted.

References:
Python Documentation - 8.17. copy — Shallow and deep copy operations