2010-06-08 33 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