2010-06-08 15 views
17

Chciałbym zainicjować słownik zestawów (w Pythonie 2.6) używając dict.fromkeys, ale wynikowa struktura zachowuje się dziwnie. Dokładniej:Niechciane zachowanie z 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])} 

I oczywiście nie chcę, aby dodać 3 do wszystkich zbiorów, tylko do zestawu, który odpowiada x[5]. Oczywiście mogę uniknąć problemu, inicjując x bez , ale chciałbym zrozumieć, czego tu brakuje.

+4

Wszystkie są takie same. Zestawy, listy, słowniki i wszelkie inne obiekty są typami odniesienia, a po przypisaniu ich do innej zmiennej kopiowane jest tylko odwołanie, a nie rzeczywisty obiekt. 'odkeys' musi użyć przypisania, aby powiązać zestaw z każdym kluczem, ale jak widzisz, to nie kopiuje zestawu. Nie jestem pewien, jak sobie z tym poradzić, pomijając tworzenie słownika w inny sposób. –

Odpowiedz

14

Drugi argument do dict.fromkeys to tylko wartość. Stworzyłeś słownik, który ma ten sam zestaw jako wartość dla każdego klucza. Prawdopodobnie rozumiesz, jak to działa:

>>> a = set() 
>>> b = a 
>>> b.add(1) 
>>> b 
set([1]) 
>>> a 
set([1]) 

Widzisz to samo zachowanie; w twoim przypadku, x[0], x[1], x[2] (itp) są różne sposoby, aby uzyskać dostęp do tego samego obiektu set.

To jest trochę łatwiej zobaczyć z przedmiotami, których reprezentacja ciąg zawiera swój adres pamięci, gdzie można zobaczyć, że są one identyczne:

>>> dict.fromkeys(range(2), object()) 
{0: <object object at 0x1001da080>, 
1: <object object at 0x1001da080>} 
0

Powodem jego pracy jest to, że w ten sposób tworzy obiekt set([]) (ustawiony obiekt). Fromkeys następnie używa tego konkretnego obiektu, aby utworzyć wszystkie jego wpisy słownikowe. Rozważ:

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

Wszystkie zestawy są takie same!

+1

Powinieneś naprawdę porównywać tożsamości: 'x [0] to x [1]'. –

3

powodu this z dictobject.c:

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

The value ma swoje „zestaw ([])”, jest on analizowany tylko raz, a następnie ich wynik zliczania odwołanie do obiektu jest zwiększany i dodano do słowniku, nie ocenia go za każdym razem, gdy dodaje się do dyktatury.

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

Nie ma potrzeby "deepcopy". 'x [n] = set()' tworzy nowy zestaw dla każdej wartości. –

13

Można to zrobić za pomocą wyrażenia generatora:

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

W Pythonie 3, można użyć słownika zrozumieniem:

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

W obu przypadkach wyrażenie set() jest uwzględniany dla każdego elementu, zamiast oceniać raz i kopiować do każdego elementu.

+0

W porządku, dzięki! –

+1

+1 za dostarczenie rozwiązania, nawet jeśli zaakceptowana odpowiedź wyjaśniła to dobrze. – Randy

+0

Jeśli zamiast zestawów chcę zainicjować listy, to x = {i: [] dla i w zakresie (10)} powoduje SyntaxError, podczas gdy dykt ((i, []) dla i w zakresie (10)) nie. – Eduardo

Powiązane problemy