0

Why this little change in code make this code works differently. I'm just learning Python. Can anyone explain in simple way? Edit: I didn't realized that appending dictionary in to list is pointing to the same dictionary and not making actual copy of it. I was trying to find solution here before this post but probably formulating my problem was tad different and it might cause for experienced programmers viewing it as duplicate.

Input

# Make an empty list for storing aliens.
aliens = []
# Make 30 green aliens.
for alien_number in range(30):
    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
    aliens.append(new_alien)

for alien in aliens[0:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10
# Show the first 5 aliens:
for alien in aliens[:5]:
    print(alien)
print("...")
# Show how many aliens have been created.
print("Total number of aliens: " + str(len(aliens)))

OUTPUT

{'points': 10, 'color': 'yellow', 'speed': 'medium'}
{'points': 10, 'color': 'yellow', 'speed': 'medium'}
{'points': 10, 'color': 'yellow', 'speed': 'medium'}
{'points': 5, 'color': 'green', 'speed': 'slow'}
{'points': 5, 'color': 'green', 'speed': 'slow'}
...
Total number of aliens: 30

Now changed the code I'll initialize dictionary outside the first for loop

Input

# Make an empty list for storing aliens.
aliens = []
# HERE IS THE CHANGE
new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
# Make 30 green aliens.
for alien_number in range(30):
    aliens.append(new_alien)

for alien in aliens[0:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10
# Show the first 5 aliens:
for alien in aliens[:5]:
    print(alien)
print("...")
# Show how many aliens have been created.
print("Total number of aliens: " + str(len(aliens)))

Output

{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}   
...
Total number of aliens: 30

Why in second is the whole dictionary changed and not just first 3 dictionaories?

martin
  • 25
  • 5
  • 5
    Before, you were creating a new dictionary each time through the loop. Now, you only create one dictionary, and add it to the same list 30 times. It doesn't matter which index in the list you use, it's still pointing to the same dictionary. – Brendan Abel Nov 07 '16 at 19:44
  • @BrendanAbel is correct - unlike some other languages, the `=` in Python *never* makes a copy of anything. The `{'color': 'green', 'points': 5, 'speed': 'slow'}` expression creates a new `dict`, and in your original code you run that 30 times, but in the second you run it only once. – Mark Ransom Nov 07 '16 at 19:49
  • Ahh geez now I get it @Brendan ...thanks...read your comment 3* more. I'm bit slow today. Thanks very much for explaining it ;) – martin Nov 07 '16 at 19:55
  • Possible duplicate of [How to copy a dictionary and only edit the copy](http://stackoverflow.com/questions/2465921/how-to-copy-a-dictionary-and-only-edit-the-copy) – TemporalWolf Nov 07 '16 at 20:02
  • This also means doing `aliens.append(newalien.copy())` will do what you want. – TemporalWolf Nov 07 '16 at 20:03
  • @TemporalWolf Thanks, I was thinking how to make it work in the second snippet. – martin Nov 07 '16 at 20:11
  • @martin it's worth mentioning that `.copy()` does a shallow copy. If you want to copy nested structures you need to use something like `import copy; copy.deepcopy(aliens)`. The linked question explains more. – TemporalWolf Nov 07 '16 at 20:17

1 Answers1

0

The point with your code is that when you build the first dictionary, it creates it and stores a reference to it in the new_alien variable. Please note that this is not the object itself but a reference to the object. In the first snippet you created a new dictionary every time and appended a reference to the new dictionary to the aliens list. In the second case you were creating only one dictionary and appending the reference to that same dictionary five times to the list. This explains why in the second case you were actually modifying the same dictionary. Let's see this example:

dct = {'a': 10, 'b': 20}  # in this moment the dictionary is initialised and a reference to it is stored in the variable dct (let's call this reference 0x01)
# dct = 0x01
lst = []
for i in range(5):
    lst.append(dct)
# at the end of the loop lst = [0x01, 0x01, 0x01, 0x01, 0x01]
lst[0]['a'] = 20  # this means take lst[0] (which is 0x01) interpret it as a dictionary and change the value at `'a'` to 20

so this change is going to affect the only dictionary you have create, which is dct. In the case of your first snippet, instead, you were creating 5 different dictionaries and modified only a part of them, see this example:

lst = []
for i in range(5):
    new_dict = {'a': 10, 'b': 20}
    lst.append(new_dict)
# by the end of the loop, lst will have references to five different dictionaries, for instance lst = [0x01, 0x02, 0x03, 0x04, 0x05]

so in this case modifying one of the entries will not affect the others

Simone Bronzini
  • 1,057
  • 1
  • 12
  • 23