I am trying to initialise a dict of empty lists in order to append to them, however this has led to some strange behaviour:
def solution(A):
d = dict.fromkeys(set(A), [])
for i in range(len(A)):
d[A[i]].append(i)
print(d)
input:
[3, 4, 3, 2, 3, -1, 3, 3]
output:
{2: [0, 1, 2, 3, 4, 5, 6, 7],
3: [0, 1, 2, 3, 4, 5, 6, 7],
4: [0, 1, 2, 3, 4, 5, 6, 7],
-1: [0, 1, 2, 3, 4, 5, 6, 7]}
dict.fromkeys seems to initialise all lists as a pointer to the same list under the hood, is this what is happening? How can I know this would be the behaviour/why would this be the behaviour? Is there a way to tell the interpreter not to do this?
3 Answers 3
How can I know this would be the behaviour
The Python documentation says it and warns you:
classmethod fromkeys(iterable[, value])
Create a new dictionary with keys from iterable and values set to value.
fromkeys()is a class method that returns a new dictionary. value defaults toNone. 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.
Comments
All your lists are the same object. [] is just a literal expression that gets evaluated before it is passed to the dict.fromkeys. How can the function know how to duplicate any random object?
If you want that behaviour, choose a collections.defaultdict:
from collections import defaultdict
def solution(A):
d = defaultdict(list) # `list` is a factory funtion that can be called repeatedly
for i in range(len(A)):
d[A[i]].append(i)
print(d)
# or, if you dislike the output (note, it is a dict already)
# print(dict(d))
2 Comments
copy method like list.In fromkeys, if the value provided is mutable (like a list) each key's value will be a reference to the same object, hence all of the values being updated at once. You can test this by appending to the original list to yield the same result as you'd been getting:
def solution(A):
lst = []
d = dict.fromkeys(set(A), lst)
for i in range(len(A)):
lst.append(i)
print(d)
Apart from other answers provided, you could go the dictionary comprehension route:
def solution(A):
d = { key : [] for key in A }
for i in range(len(A)):
d[A[i]].append(i)
print(d)
defaultdict(list)can be used here.