2016-12-04 5 views
1

Dies scheint eine ziemlich geradlinige Verwendung von setdefault und defaultdict zu sein, die ich nicht verstehen kann, wäre es toll, wenn jemand erklären könnte, "warum" der Code unten funktioniert.Verhalten von DefaultDict in Python 2.7

d = {} 

for name in ['foo', 'bar', 'bars']: 
    t = d 
    for char in name: 
     t = t.setdefault(char,{}) # Should be a empty {} 

print d 
# Prints {'b': {'a': {'r': {'s': {}}}}, 'f': {'o': {'o': {}}}} 

Ich bin nicht in der Lage zu verstehen, wie dieses Stück Code funktioniert. Wenn die Zeile t = t.setdefault (char, {}) ausgeführt wird, sollte sie t ein leeres Wörterbuch zuweisen, aber wie wirkt es sich auf d aus, so dass d zu einem verschachtelten Wörterbuch wird?

Auch was wäre das Äquivalent von dem oben, wenn ich das defaultdict verwenden würde. Ich kam mit dieser, die falsch ist:

d1 = defaultdict(dict) 

for name in ['foo', 'bar', 'bars']: 
    t1 = d1 
    for char in name: 
     t1 = t1[char] 

print d1 

Es wäre toll, wenn jemand als darauf hinzuweisen könnte, wie man defaultdicts

+0

Wie bei Listen, 't' und 'd' beide auf das gleiche dict-Objekt. Wenn sich "t" ändert, ändert sich auch "d". –

+0

Ok, ich verstehe jetzt die Idee. Wenn also die Anweisung t = t.setdefault (char, {}) ausgeführt wird, mutiert der Teil ** t.setdefault (char, {}) ** das Wörterbuch d, und da die rechte Seite zu {} auswertet, ist t einstellen {}. Wäre dieses Verständnis korrekt? –

+1

@VikashRajaSamuelSelvin: Ihr Verständnis ist richtig. Das Programm würde genauso funktionieren und klarer sein, wenn es die Variable "t" überhaupt nicht benutzt hätte, und nur ein '_ = d.setdefault (char, {})'. Die Unterstreichungsvariable würde einen Hinweis geben, dass diese Zuweisung gerade weggeworfen wurde (was ist). – Gerrat

Antwort

1

ich durch die Schleife einen Schritt zu einer Zeit gehen werde, und erklären, wie sie die verschachtelten dicts zuweisen geht:

name = 'foo' 
    t = d # both t and d point to the same empty dict object 
    char = 'f' 
     t = t.setdefault(char,{}) 
     # the first thing evaluated is the right hand side: 
     # now d['f'] = {}, since that key wasn't in the dict 
     # t points to the same object here 
     # now the result of the left side (a new empty dict) is assigned to `t`. 
     # this empty dict is also the exact *same* object referenced by d['f'] as well though! 
     # so at this point d['f'] = {}, and t = {}, and both those dicts are the same! 
    char = 'o' 
     t = t.setdefault(char,{}) 
     # eval the right side again, so now t['o'] = {}, but remember d['f'] == t 
     # so really d['f'] = {'o':{}} 
     # and again we assign the result of the right side to a brand new `t` 
     # so now d['f']['o'] = {}, and t['o'] = {}, and these empty dicts are 
     # again referencing the same object 
    char = 'o' 
     t = t.setdefault(char,{}) 
     # our `t` from last time is empty, so it gets assigned the same as before 
     # and now d['f']['o']['o'] = {} 
name = 'bar' 
    t = d # re-start this, but with d['f']['o']['o'] = {} 
    char = 'b' 
    #...everything proceeds as before - since 'b' is not in `d`, 
    # we start generating nested dicts again 
    # ... 
... 
name = 'bars' 
    # main difference here is that d['b']['a']['r'] exists, 
    # so we end up just adding the 's':{} to the end 

Was die defaultdict Äquivalent, das ist ein wenig komplizierter. Die Frage ist, was Sie brauchen defaultdict ist all-the-Art-down

ich einen Weg gefunden, das zu tun, mit einer kleinen Funktion here

from collections import defaultdict 

def fix(f): 
    return lambda *args, **kwargs: f(fix(f), *args, **kwargs) 

d1 = fix(defaultdict)() 

for name in ['foo', 'bar', 'bars']: 
    t1 = d1 
    for char in name: 
     t1 = t1[char] 

print d1 
+0

Gute Erklärung! Ich kann nicht mehr sagen. Vielen Dank. –

+0

Das ist großartig! Danke für den ausführlichen Spaziergang. Ich markiere das als Antwort. –

+0

@VikashRajaSamuelSelvin :. Vielen Dank. Ich habe den zweiten Teil Ihrer Frage ebenfalls beantwortet. Es ist ein wenig komplizierter, aber funktioniert genauso. – Gerrat

0

Für den ersten Teil verstehen sollte, t = d die Linie macht keine Kopie von d. Es erstellt nur einen neuen Verweis auf d und speichert es in t. t und d beziehen sich jetzt auf das gleiche Objekt; Mit anderen Worten, Sie haben nur ein Objekt, aber zwei Namen für dieses Objekt. Da das Objekt ein veränderbares Objekt ist (in diesem Fall ein Dict), ändert sich auch die td, da nur ein Objekt vorhanden ist. Obwohl das hier notwendig ist, wenn Sie aus irgendeinem Grund in einem anderen Code eine Kopie eines veränderbaren Objekts erstellen und die Kopie bearbeiten möchten, ohne das Original zu ändern, müssen Sie import copy verwenden und copy.deepcopy() verwenden.

Auf der zweiten erwartet der defaultdict()-Konstruktor als sein erstes Argument eine aufrufbar, die keine Argumente akzeptiert und den Standardwert zurückgibt. In diesem Fall müsste dieser Rückgabewert jedoch ein anderes defaultdict sein, mit einer Callback-Funktion, die ein anderes defaultdict zurückgibt, mit einer Callback-Funktion, die noch eine weitere zurückgibt ... usw. Es ist eine unendliche Rekursion.

Es gibt also kein defaultdict, das diesem Code entspricht. Stattdessen ist die Originalversion mit setdefault und plain dicts wahrscheinlich die beste und pythonischste Art, es zu tun.

0

wie setdefault im Wörterbuch funktioniert

# case 1 
d = {} 
temp = d.setdefault("A") 
print "d = ", d 
print "temp = ", temp 
print "id of d = ", id(d), "id of temp = ", id(temp) 
# output 
d = {'A': None} 
temp = None 
id of d = 140584110017624, id of temp = 9545840 # memory locations of d, temp 

# case 2 
d = {} 
temp = d.setdefault("A", "default Value") 
print "d = ", d 
print "temp = ", temp 
print "id of d = ", id(d), "id of temp = ", id(temp) 
# output 
d = {'A': "default Value"} 
temp = "default Value" 
id of d = 140584110017624, id of temp = 9545840 # memory locations of d, temp 

ich Ihren Code t=d bedeutet Speicherplatz von t und d beide gleich sind.
also, wenn der Code t = t.setdefault(char,{}) Excecutes ersten t.setdefault(char,{}) ausführt und ändert den Inhalt in Speicherplatz von t, dann gibt es den Inhalt dann weist es den neuen Speicherort auf den Namen t und weist ihm den zurückgegebenen Wert. Speicherplatz von t und d sind gleich das ist der Grund d wird betroffen.