2010-06-08 9 views
17

Ich möchte ein Wörterbuch von Sätzen initialisieren (in Python 2.6) mit dict.fromkeys, aber die resultierende Struktur verhält sich seltsam. Genauer gesagt:Unerwünschte Verhalten von 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])} 

Ich möchte natürlich nicht 3 bis alle Sätze zu dem Satz, nur hinzufügen, die zu x[5] entspricht. Natürlich kann ich das Problem vermeiden, indem ich x ohne fromkeys initialisiere, aber ich möchte verstehen, was ich hier vermisse.

+4

Sie sind alle gleich gesetzt. Sätze, Listen, Wörterbücher und andere Objekte sind Referenztypen, und wenn Sie sie einer anderen Variablen zuweisen, wird nur die Referenz kopiert, nicht das eigentliche Objekt. 'fromkeys' müssen eine Zuordnung verwenden, um den Satz mit jedem Schlüssel zu verbinden, aber wie Sie sehen, kopiert dies den Satz nicht. Ich bin mir nicht sicher, wie ich das umgehen soll, abgesehen davon, dass ich das Wörterbuch auf andere Weise erstellt habe. –

Antwort

14

Das zweite Argument für dict.fromkeys ist nur ein Wert. Sie haben ein Wörterbuch erstellt, in dem der Wert gleich als Wert für jeden Schlüssel festgelegt ist. Vermutlich verstehst du, wie das funktioniert:

du siehst das gleiche Verhalten dort; in Ihrem Fall, x[0], x[1], x[2] (etc) sind alle verschiedenen Möglichkeiten, um genau das gleiche set Objekt zugreifen.

Dieses etwas einfacher ist, mit Objekten, deren String-Darstellung enthält die Speicheradresse, um zu sehen, wo man sehen kann, dass sie identisch:

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

Der Grund seiner auf diese Weise arbeiten, ist, dass set([]) ein Objekt erstellt (ein gesetztes Objekt). Fromkeys verwendet dann dieses spezifische Objekt, um alle seine Wörterbucheinträge zu erstellen. Bedenken Sie:

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

Alle Sätze sind gleich!

+1

Sie sollten wirklich Identitäten vergleichen: 'x [0] ist x [1]'. –

3

Wegen this vom 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 ist Ihr "Set ([])", es nur einmal bewertet wird, dann wird ihr Ergebnis Objektreferenzzähler erhöht und dem Wörterbuch hinzugefügt, es wertet es nicht jedes Mal aus, wenn es in das Diktat eingefügt wird.

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

Keine Notwendigkeit für 'decopy'. 'x [n] = set()' erstellt für jeden Wert eine neue Menge. –

13

Sie können dies mit einem Generator Ausdruck:

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

In Python 3 Sie ein Wörterbuch Verständnis verwenden:

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

In beiden Fällen der Ausdruck set() ausgewertet für jedes Element, anstatt einmal ausgewertet und auf jedes Element kopiert zu werden.

+0

Okay, danke! –

+1

+1 für die Bereitstellung der Lösung auch nach der akzeptierten Antwort erklärte es gut. – Randy

+0

Wenn ich anstelle von Sätzen Listen initialisieren will, verursacht x = {i: [] für i im Bereich (10)} SyntaxError, während dict ((i, []) für i im Bereich (10)) nicht gilt. – Eduardo