来自dict.fromkeys的不需要的行为

时间:2010-06-08 19:14:38

标签: python

我想使用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]对应的集合。当然,我可以通过在没有x的情况下初始化fromkeys来避免这个问题,但我想了解我在这里缺少的内容。

5 个答案:

答案 0 :(得分:16)

您可以使用生成器表达式执行此操作:

x = dict( (i,set()) for i in range(10) )

在Python 3中,您可以使用字典理解:

x = { i : set() for i in range(10) }

在这两种情况下,都会为每个元素计算表达式set(),而不是对其进行一次计算并复制到每个元素。

答案 1 :(得分:15)

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>}

答案 2 :(得分:3)

由于dictobject.c

中的this
while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash))
{
            Py_INCREF(key);
            Py_INCREF(value);
            if (insertdict(mp, key, hash, value))
                return NULL;
}

value是你的“set([])”,它只被评估一次,然后它们的结果对象引用计数递增并添加到字典中,它不会在每次添加到字典时对其进行评估字典。

答案 3 :(得分:0)

它以这种方式工作的原因是set([])创建了一个对象(一个集合对象)。然后,Fromkeys使用该特定对象创建其所有字典条目。考虑:

>>> x
{0: set([]), 1: set([]), 2: set([]), 3: set([]), 4: set([]), 5: set([]), 
6: set([]), 7: set([]), 8: set([]), 9: set([])}
>>> x[0] is x[1]
True

所有套装都是一样的!

答案 4 :(得分:0)


#To do what you want:

import copy
s = set([])
x = {}
for n in range(0,5):
  x[n] = copy.deepcopy(s)
x[2].add(3)
print x

#Printing
#{0: set([]), 1: set([]), 2: set([3]), 3: set([]), 4: set([])}