4

I am not sure if this is a bug or a feature. I have a dictionary to be initialized with empty lists.

Lets say

keys =['one','two','three']
sets = dict.fromkeys(keys,[])

What I observed is if you append any item to any of the lists all the lists are modified.

sets = dict.fromkeys(['one','two','three'],[])
sets['one'].append(1)

sets

{'three': [1],'two': [1], 'one': [1]}

But when I do it manually using loop,

for key in keys:
      sets[key] = []
sets['one'].append(1)

sets

{'three': [], 'two': [], 'one': [1]}

I would think the second behavior should be the default.

3
  • 6
    This question comes in so many different shapes and forms. Commented May 1, 2012 at 16:55
  • It's likely you would have been best served here by collections.defaultdict. Declare it like this: sets = collections.defaultdict(list). Commented May 1, 2012 at 17:16
  • why are you using lists instead of sets for a variable named sets? Commented May 1, 2012 at 17:57

2 Answers 2

10

This is how things work in Python.

When you use fromkeys() in this manner, you end with three references to the same list. When you change one list, all three appear to change.

The same behaviour can also be seen here:

In [2]: l = [[]] * 3

In [3]: l
Out[3]: [[], [], []]

In [4]: l[0].append('one')

In [5]: l
Out[5]: [['one'], ['one'], ['one']]

Again, the three lists are in fact three references to the same list:

In [6]: map(id, l)
Out[6]: [18459824, 18459824, 18459824]

(notice how they have the same id)

Sign up to request clarification or add additional context in comments.

5 Comments

The easiest way to explain this, I find, is it's exactly the same as doing mylist = [] followed by sets = dict.fromkeys(keys, mylist) - just as in that case, where mylist is the value for every key, when you pass an empty list [] it's the same empty list every time. The reason for this is that in Python, variables are always references.
That is why defaultdict uses a factory of default values, not the value itself. It's strange that dict.fromkeys doens't follow the same technique.
Not so strange, the help states: dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
@MikeyB I mean it would be better if they implemented dict.fromkeys the same way as defaultdict. With the current implementation of dict.fromkeys coders have to use some roundabout ways to achive the necessary functionality.
Thanks for the comments. I just found it to be a weird behavior from python.
5

Other answers covered the 'Why', so here's the how.

You should use a comprehension to create your desired dictionary:

>>> keys = ['one','two','three']
>>> sets = { x: [] for x in keys }
>>> sets['one'].append(1)
>>> sets
{'three': [], 'two': [], 'one': [1]}

For Python 2.6 and below, the dictionary comprehension can be replaced with:

>>> sets = dict( ((x,[]) for x in keys) )

2 Comments

otherwise the following might help: dict(zip(keys, ([] for x in keys)))
@MikeyB Sorry my mistake. Also dict([(x,[]) for x in keys]) Not sure which is faster, generator or list comp

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.