dict.fromkeys的意外行为

23 投票
5 回答
5127 浏览
提问于 2025-04-15 23:42

我想用 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>}

撰写回答