dict.fromkeys的意外行为
我想用 dict.fromkeys
来初始化一个集合的字典(在 Python 2.6 中),但是得到的结构表现得有点奇怪。更具体来说:
>>>> x = {}.fromkeys(range(10), set([]))
>>>> x
{0: set([]), 1: set([]), 2: set([]), 3: set([]), 4: set([]), 5: set([]), 6: set([]), 7: set([]), 8: set([]), 9: set([])}
>>>> x[5].add(3)
>>>> x
{0: set([3]), 1: set([3]), 2: set([3]), 3: set([3]), 4: set([3]), 5: set([3]), 6: set([3]), 7: set([3]), 8: set([3]), 9: set([3])}
我显然不想把 3 加到所有的集合中,只想加到对应于 x[5]
的那个集合里。当然,我可以通过不使用 fromkeys
来初始化 x
来避免这个问题,但我想搞清楚我在这里漏掉了什么。
5 个回答
3
因为在这个文件中提到的内容:
while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash))
{
Py_INCREF(key);
Py_INCREF(value);
if (insertdict(mp, key, hash, value))
return NULL;
}
这里的value
就是你用“set([])”创建的东西,它只会被计算一次,然后它的结果对象的引用计数会增加,并被添加到字典里。也就是说,每次添加到字典时,它并不会重新计算。
19
你可以用生成器表达式来做到这一点:
x = dict( (i,set()) for i in range(10) )
在Python 3中,你可以使用字典推导式:
x = { i : set() for i in range(10) }
在这两种情况下,set()
这个表达式会对每个元素单独计算,而不是只计算一次然后复制给每个元素。
19
在使用 dict.fromkeys
的时候,第二个参数其实就是一个值。你创建了一个字典,这个字典里的每个键都有相同的值。大概你明白这个是怎么回事:
>>> a = set()
>>> b = a
>>> b.add(1)
>>> b
set([1])
>>> a
set([1])
你在这里看到的行为是一样的;在你的例子中,x[0]
、x[1]
、x[2]
(等等)都是访问同一个 set
对象的不同方式。
如果用一些对象,它们的字符串表示包含内存地址,这样就更容易看出它们是相同的:
>>> dict.fromkeys(range(2), object())
{0: <object object at 0x1001da080>,
1: <object object at 0x1001da080>}