How do I initialize a dictionary of empty lists in Python?

Question:

My attempt to programmatically create a dictionary of lists is failing to allow me to individually address dictionary keys. Whenever I create the dictionary of lists and try to append to one key, all of them are updated. Here’s a very simple test case:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

Actual result: {0: ['hello'], 1: ['hello']}

Expected result: {0: [], 1: ['hello']}

Here’s what works

data = {0:[],1:[]}
data[1].append('hello')
print data

Actual and Expected Result: {0: [], 1: ['hello']}

Why is the fromkeys method not working as expected?

Asked By: Martin Burch

||

Answers:

Try using a defaultdict instead:

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

This way, the keys don’t need to be initialized with empty lists ahead of time. The defaultdict() object instead calls the factory function given to it, every time a key is accessed that doesn’t exist yet. So, in this example, attempting to access data[1] triggers data[1] = list() internally, giving that key a new empty list as its value.

The original code with .fromkeys shares one (mutable) list. Similarly,

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print(data)

would output {0: [1, 2], 1: [1, 2]}. This is called out in the dict.fromkeys() documentation:

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.

Another option is to use the dict.setdefault() method, which retrieves the value for a key after first checking it exists and setting a default if it doesn’t. .append can then be called on the result:

data = {}
data.setdefault(1, []).append('hello')

Finally, to create a dictionary from a list of known keys and a given "template" list (where each value should start with the same elements, but be a distinct list), use a dictionary comprehension and copy the initial list:

alist = [1]
data = {key: alist[:] for key in range(2)}

Here, alist[:] creates a shallow copy of alist, and this is done separately for each value. See How do I clone a list so that it doesn't change unexpectedly after assignment? for more techniques for copying the list.

Answered By: Martijn Pieters

You are populating your dictionaries with references to a single list so when you update it, the update is reflected across all the references. Try a dictionary comprehension instead. See
Create a dictionary with list comprehension in Python

d = {k : v for k in blah blah blah}
Answered By: cobie

When [] is passed as the second argument to dict.fromkeys(), all values in the resulting dict will be the same list object.

In Python 2.7 or above, use a dict comprehension instead:

data = {k: [] for k in range(2)}

In earlier versions of Python, there is no dict comprehension, but a list comprehension can be passed to the dict constructor instead:

data = dict([(k, []) for k in range(2)])

In 2.4-2.6, it is also possible to pass a generator expression to dict, and the surrounding parentheses can be dropped:

data = dict((k, []) for k in range(2))
Answered By: Sven Marnach

You can use this:

l = ['a', 'b', 'c']
d = dict((k, [0, 0]) for k in l)
Answered By: g.d.d.c

You could use a dict comprehension:

>>> keys = ['a','b','c']
>>> value = [0, 0]
>>> {key: list(value) for key in keys}
    {'a': [0, 0], 'b': [0, 0], 'c': [0, 0]}
Answered By: Blender

This answer is here to explain this behavior to anyone flummoxed by the results they get of trying to instantiate a dict with fromkeys() with a mutable default value in that dict.

Consider:

#Python 3.4.3 (default, Nov 17 2016, 01:08:31) 

# start by validating that different variables pointing to an
# empty mutable are indeed different references.
>>> l1 = []
>>> l2 = []
>>> id(l1)
140150323815176
>>> id(l2)
140150324024968

so any change to l1 will not affect l2 and vice versa.
this would be true for any mutable so far, including a dict.

# create a new dict from an iterable of keys
>>> dict1 = dict.fromkeys(['a', 'b', 'c'], [])
>>> dict1
{'c': [], 'b': [], 'a': []}

this can be a handy function.
here we are assigning to each key a default value which also happens to be an empty list.

# the dict has its own id.
>>> id(dict1)
140150327601160

# but look at the ids of the values.
>>> id(dict1['a'])
140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328

Indeed they are all using the same ref!
A change to one is a change to all, since they are in fact the same object!

>>> dict1['a'].append('apples')
>>> dict1
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
>>> id(dict1['a'])
>>> 140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328

for many, this was not what was intended!

Now let’s try it with making an explicit copy of the list being used as a the default value.

>>> empty_list = []
>>> id(empty_list)
140150324169864

and now create a dict with a copy of empty_list.

>>> dict2 = dict.fromkeys(['a', 'b', 'c'], empty_list[:])
>>> id(dict2)
140150323831432
>>> id(dict2['a'])
140150327184328
>>> id(dict2['b'])
140150327184328
>>> id(dict2['c'])
140150327184328
>>> dict2['a'].append('apples')
>>> dict2
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}

Still no joy!
I hear someone shout, it’s because I used an empty list!

>>> not_empty_list = [0]
>>> dict3 = dict.fromkeys(['a', 'b', 'c'], not_empty_list[:])
>>> dict3
{'c': [0], 'b': [0], 'a': [0]}
>>> dict3['a'].append('apples')
>>> dict3
{'c': [0, 'apples'], 'b': [0, 'apples'], 'a': [0, 'apples']}

The default behavior of fromkeys() is to assign None to the value.

>>> dict4 = dict.fromkeys(['a', 'b', 'c'])
>>> dict4
{'c': None, 'b': None, 'a': None}
>>> id(dict4['a'])
9901984
>>> id(dict4['b'])
9901984
>>> id(dict4['c'])
9901984

Indeed, all of the values are the same (and the only!) None.
Now, let’s iterate, in one of a myriad number of ways, through the dict and change the value.

>>> for k, _ in dict4.items():
...    dict4[k] = []

>>> dict4
{'c': [], 'b': [], 'a': []}

Hmm. Looks the same as before!

>>> id(dict4['a'])
140150318876488
>>> id(dict4['b'])
140150324122824
>>> id(dict4['c'])
140150294277576
>>> dict4['a'].append('apples')
>>> dict4
>>> {'c': [], 'b': [], 'a': ['apples']}

But they are indeed different []s, which was in this case the intended result.

Answered By: Shawn Mehan

You could use this:

data[:1] = ['hello']
Answered By: Conner Dassen
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.