2016-12-04 84 views
1

這似乎是setdefault和defaultdict的一個非常直接的用法,我無法理解,如果有人能夠解釋下面的代碼爲什麼會起作用,那將是非常棒的。Python 2.7中DefaultDict的行爲

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': {}}}} 

我無法理解這段代碼如何工作。當行t = t.setdefault(char,{})執行時,它應該爲t指定一個空字典,但是它如何影響d以便d最終成爲嵌套字典?

另外,如果我使用defaultdict,那麼上面的等價物會是什麼。我想出了這個這是錯誤的:

d1 = defaultdict(dict) 

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

print d1 

這將是巨大的,如果有人可以作爲一個應該如何理解defaultdicts

+0

與列表,'t'和'D'都指向相同的字典對象。當「t」改變時,「d」也改變。 –

+0

好的,我現在就明白了。因此,當執行語句t = t.setdefault(char,{})時,部分** t.setdefault(char,{})**突變字典d,並且由於右側評估爲{},因此t是設置 {}。這種理解是否正確? –

+1

@VikashRajaSamuelSelvin:你的理解是正確的。該程序的工作原理是一樣的,如果它根本不使用't'變量,並且只是做了一個'_ = d.setdefault(char,{})',它就會更清晰。下劃線變量會給出一個線索,那麼這個任務剛剛被扔掉(這是)。 – Gerrat

回答

1

我會通過一個一步來走,並解釋它是如何進行分配的嵌套類型的字典:

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 

至於相當於defaultdict,這是一個有點棘手。 問題是你需要defaultdict的全閉塞下

我發現了一個辦法做到這一點有一點功能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

好解釋!我不能再多說了。謝謝。 –

+0

太棒了!感謝您的詳細介紹。我將此標記爲答案。 –

+0

@VikashRajaSamuelSelvin :.謝謝。我回答了你的問題的第二部分。這有點棘手,但工作原理是一樣的。 – Gerrat

0

對於第一部分指出,行t = d不會使副本d。它只創建一個新的參考d並將其存儲在ttd現在指的是同一個對象;換句話說,你只有一個對象,但是這個對象有兩個名字。由於該對象是可變對象(在本例中爲字典),因爲只有一個對象,所以更改t也會更改d。雖然這是必要的,但如果由於某種原因在其他代碼中想要製作可變對象的副本並在不修改原稿的情況下對副本進行操作,則需要import copy並使用copy.deepcopy()

第二,defaultdict()構造函數期望作爲其第一個參數的可調用對象不接受參數並返回默認值。但是,對於這種情況,該返回值需要是另一個可調用的默認代碼,並返回另一個可調用的默認代碼,以返回另一個默認代碼......等等。這是無限遞歸。

所以沒有相當於這個代碼的defaultdict。相反,與setdefault和普通字典的原始版本可能是最好的和最Python的方式來做到這一點。

0

setdefault如何在字典

# 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 

我你的代碼t=d結構T的存儲位置和d都是相同的。
因此,當代碼t = t.setdefault(char,{})先執行時,t.setdefault(char,{})執行並更改t的內存位置中的內容,然後它將內容返回,然後將其指定新的內存位置命名爲t並將返回的值分配給它。 td的內存位置相同,這就是d正在受到影響的原因。