1

This is very weird, how can we add a dictionary in a list within a loop ?! when I try this code, for example, it works just fine :

a = {}
b = []
for i in range(5) :
    a['number'] = i
    print (a)

Output :

{'number': 0}
{'number': 1}
{'number': 2}
{'number': 3}
{'number': 4}

So , i want to add this output dictionary to a list , so i tried this code :

for i in range(5) :
    a['number'] = i
    b.append(a)

but when i try to print the list .. this is what i get :

[{'number': 4}, {'number': 4}, {'number': 4}, {'number': 4}, {'number': 4}]

WHY ?! this is very weird .. can someone explain ?!

Joe Ferndz
  • 8,417
  • 2
  • 13
  • 33
  • 3
    You are appending the *same* dictionary multiple times. When you update the key/value, you are updating the original dict. – Mark Dec 21 '20 at 18:06
  • Does this answer your question? [List of lists changes reflected across sublists unexpectedly](https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly) – quamrana Dec 21 '20 at 19:44

4 Answers4

3
for i in range(5) :
    a['number'] = i
    b.append(a)

Each iteration modifies dict a, then appends a to the list. I think you believe what is appended to the list should be a copy, or a snapshot of a, but this is not the case. Using proper vocabulary, the parameter taken by list.append is a reference, not a value. So at the end, the list contains five identical references to the same dict object, which contains {'number': 4}.

Objects are passed by reference in python, which means:

a={}
b=a
a[1]=2
print(b)
{1: 2}

b and a reference the same object... If you don't want that, you have to make a copy.

a={}
b=a.copy()
a[1]=2
print(b)
{}

So for your example to do what you think it should do, you could write:

for i in range(5) :
    a['number'] = i
    b.append(a.copy())

Or you could make a new dict at each iteration:

for i in range(5) :
    b.append({"number":i})

Note that .copy() only makes a shallow copy, if the dict contains references to other objects they will still reference the same object. It is up to you to decide if you want to use pass-by-reference or pass-by-value (ie, copy) with objects. Obviously, doing lots of value copies is slower.

bobflux
  • 11,123
  • 3
  • 27
  • 27
1

b is a list that contains five references to the dictionary a. These are not copied snapshots of the dict; they are references to the active, mutable object a. Your final output prints the same reference five times; they must be identical, each one referring to the most recent values in a.

If you want a snapshot of the dict, you need to copy it to a new reference that you don't change. Look up how to copy various structures. In general, copy and deepcopy are the methods you need.

Prune
  • 76,765
  • 14
  • 60
  • 81
1

the first one, namely this

a = {}
b = []
for i in range(5) :
    a['number'] = i
    print(a)

doesn't do what you thing it does, first it add the key 'number' with the value 0 to the dictionary a and then print said dictionary, the second time it change the value associated with said key to 1 and once more print it, and soo on, is not creating a list or a new dictionary, it just print the current state of the dictionary

In the second one

for i in range(5) :
    a['number'] = i
    b.append(a)

you're appending to the list the same dictionary multiple times, and because what the list really store is a reference to the real object so you end with

 [{'number': 4}, {'number': 4}, {'number': 4}, {'number': 4}, {'number': 4}]

and because they're are referencing the same object they of course will be same, and if you change one, you will see the change reflected on all entries in the list because once more the point to the same dictionary.

If you want that your list a to have different dictionary in it, you need to create a new one each time, like this for example:

b = []
for i in range(5) :
    a = {'number': i}
    b.append(a)
print(b)# [{'number': 0}, {'number': 1}, {'number': 2}, {'number': 3}, {'number': 4}]

or as a one liner with list comprehension

b = [{"number":i} for i  in range(5)]
Copperfield
  • 8,131
  • 3
  • 23
  • 29
0

Notice how your first example prints the same dict object (named a) on each loop. You are modifying that dict each time you loop. If you understand this, it shouldn't be suprising that you are adding a reference to the same dict object 5 times to the list b. To add 5 new dictionaries, just create a new dict on each iteration:

a = []
for i in range(5):
   a.append({'number': i})
Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268