Title: dict.fromkeys - Odd logic when passing second dict.fromkeys as value
Type: behavior Stage:
Components: Interpreter Core Versions: Python 2.4, Python 2.3, Python 2.5
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: dohertywa, rhettinger
Priority: normal Keywords:

Created on 2007-10-16 20:27 by dohertywa, last changed 2007-10-16 22:35 by rhettinger. This issue is now closed.

File name Uploaded Description Edit
problem-report.txt dohertywa, 2007-10-16 20:27
Messages (2)
msg56507 - (view) Author: Adam Doherty (dohertywa) Date: 2007-10-16 20:27

I'm am trying to conduct some tests on a list of data that checks for
the position of values in list elements using the bisect module.  To
store the results of these tests for output to a template I have build a
dictionary with 47 keys the values of which are dictionaries themselves.
 These inner dictionaries contain 7 keys that initially are valued at
zero.  Looping through the data in my list I check for values from 1 to
47 and if I find the value I am looking for I lookup it's position in
the row using the bisect module.  Using the current value I am looking
for and the position returned from bisect I increase the value in the
matching dictionary key value position by 1. Now for speed I have built
the dictionary using d =
dict.fromkeys(xrange(1,48),dict.fromkeys(xrange(1,8),0)) as this gives
the desired result.  Unfortunately when I run my test for values each
value in the dictionary is listed as the total number of rows in the
data list.  This is not the desired result which is correctly achieved
d = {}
for x in xrange(1,48):
    d[x] = dict.fromkeys(xrange(1,8),0)

I have included output from IDLE to demonstrate the problem.
msg56508 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2007-10-16 22:35
This isn't a bug.  Writing
"dict.fromkeys(xrange(1,48),dict.fromkeys(xrange(1,8),0))" results in
the inner expression being evaluated just once and then passed to the
outer function call as a fully evaluated argument.  As a result, the
*same* dictionary is being used over and over again.

People commonly encounter similar issue when they try to create
initialized list-of-lists with something like s=[[0]*10] which repeats
ten of the *same* lists.  Instead they should write something like: 
s=[[0] for i in range(10)] which creates *distinct* inner lists.

For you application, consider using a defaultdict which can call a
function as needed to create new, distinct values:

  d = defaultdict(lambda: dict.fromkeys(xrange(1,8), 0))
Date User Action Args
2007-10-16 22:35:47rhettingersetstatus: open -> closed
resolution: not a bug
messages: + msg56508
nosy: + rhettinger
2007-10-16 20:27:31dohertywacreate