1

Please tell me what I am doing wrong!

I wish to update entries in a dictionary from a list. Here are my (simplified) dictionary and list:

scene_dict = {0: {'speaker': None, 'listener': None}, 1: {'speaker': None, 'listener': None}}
speaker_list = ["Horatio", "Marcellus"]

I want to achieve the following result:

scene_dict = {0: {'speaker': "Horatio", 'listener': None}, 1: {'speaker': "Marcellus", 'listener': None}}

which would seem to be a straight-forward problem.

In order to achieve the required result, I do the following:

for i, j in enumerate(scene_dict):
    scene_dict[j]["speaker"] = speaker_list[i]

which then gives the result I want, yeh!

print(scene_dict)

{0: {'speaker': 'Horatio', 'listener': None}, 1: {'speaker': 'Marcellus', 'listener': None}}

So, "where's the problem?" you may ask. Well, in reality, the list of names that I am dealing with is much longer. So, my dictionary needs to be correspondingly much longer, but it has the exact same form. Naturally, I don't want to define my dictionary by writing it down explicitly. I need to generate it somehow, which I do as follows:

para_dict = {"speaker": None, "listener": None}
scene_dict = {}

for k in range(0,2):
    scene_dict[k] = para_dict 

This "appears" to work fine, and "appears" to give the exact same dictionary as I started with:

print(scene_dict)

{0: {'speaker': None, 'listener': None}, 1: {'speaker': None, 'listener': None}}

However, when I now re-run the exact same for loop above on my ("exact same") generated dictionary, in order to transfer the elements of my list to the values in the dictionary

for i, j in enumerate(scene_dict):
    scene_dict[j]["speaker"] = speaker_list[i]

I get a different (wrong) result:

print(scene_dict)

{0: {'speaker': 'Marcellus', 'listener': None}, 1: {'speaker': 'Marcellus', 'listener': None}}

This time the for loop apparently equates both dictionary keys ("speaker") at the same time with the first name in the list, and then both keys with the second name in the list, i.e. it equates both with Horatio and then both with Marcellus.

I am mystified. What did I do wrong? How can I do better?

Any advice would be appreciated.

1 Answer 1

1

Ah you've been trapped by the old in python objects are all references trick!

For context every object, dict, array, Object, ect. are bound to variables as references or pointers to the actual object that is stored in memory. When you do something like

obj = {"Hello" : "I'm an object"}
obj2 = obj

You are not making a copy of obj and storing that in obj2. obj2 is just referencing obj again. If you were to print the memory address of obj and obj2 using the id function you would get the same address for each

Now for your example, when you do

para_dict = {"speaker": None, "listener": None}
scene_dict = {}

for k in range(0,2):
    scene_dict[k] = para_dict

you assign the same reference to para_dict to every entry in scene_dict. That is,

{
   0 : {"speaker" : None, "listener" : None},
   1 : {"speaker" : None, "listener" : None}
}

is actually

{
   0 : para_dict, # These are actually the same object!
   1 : para_dict
}

So when you modify your scene_dict in,

for i, j in enumerate(scene_dict):
    scene_dict[j]["speaker"] = speaker_list[i]

Your modifing every instance of para_dict you've assigned. So walking through the loop you would get.

start    | {0: {'speaker': None, 'listener': None}, 1: {'speaker': None, 'listener': None}}
1st iter | {0: {'speaker': 'Horatio', 'listener': None}, 1: {'speaker': 'Horatio', 'listener': None}}
2nd iter | {0: {'speaker': 'Marcellus', 'listener': None}, 1: {'speaker': 'Marcellus', 'listener': None}}

Now the fix is pretty simple, Instead of assigning para_dict just make the dictionary every time.

scene_dict = {}

for k in range(0,2):
    scene_dict[k] = {"speaker": None, "listener": None}

This will create a new dictionary each iteration of the loop instead of a reference to an already created one. If your para_dict gets complicated you can check out the copy function.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.