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

Each Answer to this Q is separated by one/two green lines.

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),[])
print data

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

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

Here’s what works

data = {0:[],1:[]}
print data

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

Why is the fromkeys method not working as expected?

Passing [] as second argument to dict.fromkeys() gives a rather useless result – all values in the dictionary will be the same list object.

In Python 2.7 or above, you can use a dicitonary comprehension instead:

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

In earlier versions of Python, you can use

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

Use defaultdict instead:

from collections import defaultdict
data = defaultdict(list)

This way you don’t have to initialize all the keys you want to use to lists beforehand.

What is happening in your example is that you use one (mutable) list:

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

would output {0: [1, 2], 1: [1, 2]}.

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]}

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.


#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)
>>> id(l2)

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)

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

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'])
>>> id(dict1['c'])

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)

and now create a dict with a copy of empty_list.

>>> dict2 = dict.fromkeys(['a', 'b', 'c'], empty_list[:])
>>> id(dict2)
>>> id(dict2['a'])
>>> id(dict2['b'])
>>> id(dict2['c'])
>>> 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'])
>>> id(dict4['b'])
>>> id(dict4['c'])

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'])
>>> id(dict4['b'])
>>> id(dict4['c'])
>>> dict4['a'].append('apples')
>>> dict4
>>> {'c': [], 'b': [], 'a': ['apples']}

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

You can use this:

l = ['a', 'b', 'c']
d = dict((k, [0, 0]) for k in l)

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}

You could use this:

data[:1] = ['hello']

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Leave a Reply

Your email address will not be published.