0

I came across this weird issue this morning:

>>> l = ['a', 'b', 'c']
>>> dict = dict.fromkeys(l, [])
>>> dict['a'] += [1]
>>> dict
{'a': [1], 'c': [1], 'b': [1]}

I can not explain why this happens?

yorua007
  • 803
  • 3
  • 9
  • 14

2 Answers2

4

dict.fromkeys creates a whole bunch of references to the same list. In other words, d['a'] is d['b'] etc. When you do += you extend the list in place, so modifications are seen in all the lists -- after all, they're the same list.

>>> l = ['a', 'b', 'c']
>>> d = dict.fromkeys(l, [])
>>> d
{'a': [], 'c': [], 'b': []}
>>> print [id(v) for v in d.values()]  # Note, they all have the same ID -- They're the same.
[4385936016, 4385936016, 4385936016]
>>> d['a'] += [1]
>>> d
{'a': [1], 'c': [1], 'b': [1]}

As pointed out in the comments, I didn't actually tell you how to get around this problem. If you want to initialize a dictionary with keys that are instances of mutable values, you can use a dictionary comprehension:

d = {key: [] for key in l}

Or, if you're stuck with an old version of python (before python2.7), you pass an iterable that yields 2-tuples to the dict constructor:

d = dict((key, []) for key in l)

Note that there are other variants as well that are useful to know about (colections.defaultdict, a regular dict subclass with overridden __missing__). Each of these has slightly different behaviors that I explain here. However, this should be good enough to get you going for the time being...

Community
  • 1
  • 1
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • A correct notation for what the poster was trying to do is `dict = {key: [] for key in l}`, giving each key its own unique list, i don't want to post a new answer when you've already got it nailed. – user2085282 Aug 28 '14 at 02:36
  • @user2085282 -- good point. I suppose I should offer an alternative . . . – mgilson Aug 28 '14 at 05:14
0

You might be interested in a collections.defaultdict:

In [66]: d = collections.defaultdict(list)

In [67]: d['a'] += [1]

In [68]: d
Out[68]: defaultdict(<class 'list'>, {'a': [1]})

In [69]: dict(d)
Out[69]: {'a': [1]}

In [70]: d['b']
Out[70]: []

In [71]: dict(d)
Out[71]: {'b': [], 'a': [1]}

In [72]: d['c']
Out[72]: []

In [73]: dict(d)
Out[73]: {'b': [], 'c': [], 'a': [1]}
inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241