Search code examples
pythondictionarydeep-copy

Why does changing one Python list element change all the others?


I have some code where I make a dictionary and each value is the list [0,0]. I found that incrementing any list index would increment each value's corresponding index; i.e. d[key][0] += 1 would increment each other list's 0 index.

I assumed Python's shallow copying of the list was to blame, since my dictionary was constructed using dict.fromkeys(keys, [0,0]). However, I had the same problem when using copy.deepcopy on the list. I then tried dictionary comprehension: {key:[0,0] for key in keys}. This works as I would expect; modifying one list does not affect the others.

Since I've found a solution to this issue, my actual question is why this is the case. It seems that either I'm mistaken about how deepcopy works or that it's irrelevant to the issue at hand, but I don't know what else would cause this.

This is a full snippet that reproduces the behavior:

import copy
keys = [i for i in range(5)]
l = [0,0]
d = dict.fromkeys(keys, copy.deepcopy(l))
#d = {key:[0,0] for key in keys} # this approach works as expected
d[0][0] += 1
print(d)
# expected: {0: [1, 0], 1: [0, 0], 2: [0, 0], 3: [0, 0], 4: [0, 0]}
# actual: {0: [1, 0], 1: [1, 0], 2: [1, 0], 3: [1, 0], 4: [1, 0]}

Solution

  • In this case, deepcopy is not helping the issue, it's dict.fromkeys functionality. From the documentation at https://docs.python.org/3/library/stdtypes.html#dict.fromkeys

    All of the values refer to just a single instance, so it generally doesn’t make sense for value to be a mutable object such as an empty list. To get distinct values, use a dict comprehension instead.

    You are passing in a single list to fromkeys for the value argument. So every key in the dict refers to one list, which just happened to be deepcopied from l.

    So it seems like your dict comprehension is the recommended method. Since you are making a new list for each element, with [0, 0], they stay independent.