2016-12-04 79 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正在受到影响的原因。