4

This is my code:

In [8]: b=dict.fromkeys([1,2,3,4], [])

In [9]: b[1].append(1)

In [10]: b[2].append(2)

In [11]: b[1]
Out[11]: [1, 2]

In [12]: b[2]
Out[12]: [1, 2]

In [13]: b
Out[13]: {1: [1, 2], 2: [1, 2], 3: [1, 2], 4: [1, 2]}

Whereas I would expect: {1: [1], 2: [2], 3: [], 4: []}

I guess this is maybe caused by b[X] is just a "reference", they all refer to a same list.

Then I replace [] with an int object. The result makes me more confused:

In [15]: b=dict.fromkeys([1,2,3,4], 1)

In [16]: b[1] += 1

In [17]: b[2] += 1

In [18]: b
Out[18]: {1: 2, 2: 2, 3: 1, 4: 1}

This int object 1 isn't a referance in this case.

Then I replace [] with ['a']:

In [19]: b=dict.fromkeys([1,2,3,4], ['a'])

In [20]: b[1].append(1)

In [21]: b[2].append(2)

In [22]: b
Out[22]: {1: ['a', 1, 2], 2: ['a', 1, 2], 3: ['a', 1, 2], 4: ['a', 1, 2]}

Now ['a'] is a reference again.

Can someone tells me why, and how to get expected result "{1: [1], 2: [2], 3: [], 4: []}" in the first case.

Any useful suggestions are appreciated.

2 Answers2

6

Because all values in the dict are actually references to the same list, dict.fromkeys uses the same list object and assigns it to each key. As list.append is an in-place operation so all keys are affected.

>>> b = dict.fromkeys([1,2,3,4], [])
>>> [id(x) for x in b.values()]
[158948300, 158948300, 158948300, 158948300]

So, for mutable value use a dict comprehension:

>>> b = {k:[] for k in xrange(1, 5)}
>>> [id(x) for x in b.values()]
[158945580, 158948396, 158948108, 158946764]

Or as @Bakuriu suggested, collections.defaultdict will also work fine:

>>> from collections import defaultdict
>>> dic = defaultdict(list)
>>> dic[1].append(1)
>>> dic[2].append(2)
>>> dic
defaultdict(<type 'list'>, {1: [1], 2: [2]})
>>> dic[3]
[]
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
4

They really are all references. The difference is what you do with the references.

When you use the assignment operator =, you're setting the reference to a different object. (+= works the same).

When you use append, you're modifying the object without affecting the reference. Since fromkeys gave back multiple references to the same object, the modifications are seen in all of them simultaneously.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622