2010-06-08 36 views
17

我想用dict.fromkeys初始化集合的字典(在Python 2.6中),但生成的结构行为奇怪。更具体地讲:来自dict.fromkeys的不想要的行为

>>>> 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来避免这个问题,但我想了解我在这里错过了什么。

+4

它们都是一样的。集合,列表,字典和任何其他对象都是引用类型,当您将它们分配给另一个变量时,只复制引用,而不是实际对象。 'fromkeys'必须使用赋值将该组与每个键相关联,但正如您所看到的,这不会复制该组。我不知道如何解决这个问题,除了以不同的方式创建字典。 – 2010-06-08 19:19:20

回答

14

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

所有的套都是一样的!

+1

你应该真的比较身份:'x [0]是x [1]'。 – 2010-06-08 20:19:34

3

由于thisdictobject.c的:

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

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

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([])} 
+2

不需要'deepcopy'。 'x [n] = set()'为每个值创建一个新的集合。 – 2010-06-08 20:18:24

13

您可以用生成器表达式做到这一点:

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

在Python 3,你可以使用字典解析:

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

在这两种情况下,表达set()评估为每个元素,而不是被评估一次,并复制到每个元素。

+0

好的,谢谢! – 2010-06-09 05:50:10

+1

+1提供的解决方案,即使接受的答案解释得很好。 – Randy 2013-12-23 22:51:08

+0

如果不是集合我想初始化列表,x = {i:[] for i in range(10)}导致SyntaxError,而范围(10)中的dict((i,[]))不会。 – Eduardo 2014-02-20 09:57:02