Python Forum

Full Version: Adding to the dictionary inside the for-loop - weird behaviour
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I want to write a program in which as an input I take a list consisted of words which can contain punctuation marks. I built a dictionary where keys are elements of a given list and values are empty lists. My goal is to add to these lists (inside the this dictionary) the indexes of those chars from the words which are punctuation marks. The program works partially - the problem is, that it adds the proper index to the previous list, i. e. to the list for the previous key, not for the current one. If you analyze the code, you will see what I mean:
list1 = ['!,', 'Hey!']
dict_words = dict.fromkeys(list1, [])
print(list1)
print(dict_words)
print()
for word in list1:    
  counter_punc = 0
  print('"word": ', word, ' dict_words["word"]: ', dict_words[word])
  if word.isalpha():
    print("alpha word: ", word)
  else:
    print("non-alpha word: ", word)
    for letter in word:
      if letter.isalpha():
        print("alpha letter: ", letter) #, " non-alpha word: ", word)
      else:
        counter_punc += 1
        dict_words[word].append(word.index(letter))
        print("letter: ", letter, " index: ", word.index(letter))
        print('"word": ', word, ' dict_words["word"]: ', dict_words[word])
        # dict_words[word].append()
  print("word: ", word, " counter_punc: ", counter_punc)
  print()
Iterating through the items of this dictionary:
for k,v in dict_words.items():
  print(k,v)
we obtain that for both keys we have the same value, i. e.
[0,1,3]
It is incorrect - it should be for the key
'!,'
the value
[0,1]
and for the key
'Hey!'
the value
[3]


Could anyone point me where the bug is and how to fix it?
I would be grateful for help.
It's the line :

dict_words = dict.fromkeys(list1, [])
That's causing the trouble. I'm not sure how you would use the method fromkeys differently in order to correct the problem but if you replace that line with :
dict_words = {'!,': [], 'Hey!': []}
then your code will produce the expected results.
here's what I get:
>>> list1 = ['!,', 'Hey!']
>>> dict_words = dict.fromkeys(list1, [])
>>> for k, v in dict_words.items():
...     print(f"k: {k}, v: {v}")
... 
k: !,, v: []
k: Hey!, v: []
>>>
fromdict takes a list of keys and a value and assigns that value to all keys. If you send a list as value, all keys share the same list. When you print the dictionary you can see that all keys have an empty list but you can't see that it is the same list and that the keys share it. The only way get around the problem is as @BashBedlam indicates.
spam = ['foo', 'bar']
eggs = dict.fromkeys(spam, [])
print(eggs)
print([id(item) for item in eggs.values()])
Output:
{'foo': [], 'bar': []} [140175770726664, 140175770726664]
lists are mutable and as you can see all keys refer to same object (same id). Update is reflected in all of them.

you can do eggs = {key:[] for key in spam}
alternatively, you can use collections.defaultdict with default value being list
Hey guys! Thank you all for the explanation and spending your time helping me.
Thank you: @BashBedlam, @Larz60+, @Serafim, @buran :)