1

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?

mkrieger1
24.2k7 gold badges68 silver badges84 bronze badges
asked Apr 30, 2020 at 14:36
1
  • defaultdict(list) can be used here. Commented Apr 30, 2020 at 14:38

3 Answers 3

1

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 to None. 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.

answered Apr 30, 2020 at 14:42
Sign up to request clarification or add additional context in comments.

Comments

1

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))
Ch3steR
20.8k4 gold badges34 silver badges66 bronze badges
answered Apr 30, 2020 at 14:40

2 Comments

it is not the behaviour but why would i expect it not to be able to copy?
You can pass any object, custom and all (could be a class or a function or whatever). The operation "copy" is not generally defined. Not every type has a copy method like list.
0

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)
answered Apr 30, 2020 at 14:52

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.