2016-12-04 5 views
1

Это похоже на довольно сложное использование setdefault и defaultdict, которое я не могу понять, было бы здорово, если бы кто-нибудь мог объяснить «почему» приведенный ниже код.Поведение DefaultDict в 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': {}}}} 

Я не могу понять, как работает этот кусок кода. Когда строка 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

Как со списками, 'и' Т * d' как указывают на один и тот же объект. Когда 't' изменяется, изменяется и' d'. –

+0

Хорошо, я как бы понял эту идею. Поэтому, когда выполняется инструкция t = t.setdefault (char, {}), часть ** t.setdefault (char, {}) ** мутирует словарь d, а так как правая сторона оценивает {}, t является установлен в {}. Будет ли это понимание правильным? –

+1

@VikashRajaSamuelSelvin: Ваше понимание верное. Программа будет работать одинаково и будет более ясной, если бы она не использовала переменную 't' вообще, и просто сделала' _ = d.setdefault (char, {}) '. Подчеркнутая переменная давала бы ключ к пониманию того, что это задание было просто выброшено (то есть есть). – Gerrat

ответ

1

Я буду ходить через петлю один шаг за один раз, и объяснить, как это происходит, чтобы назначить вложенную dicts:

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 все-The-путь вниз

Я нашел способ сделать это с небольшим количеством функции 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 и сохраняет ее в t. t и d теперь относятся к одному и тому же объекту; другими словами, у вас есть только один объект, но два имени для этого объекта. Поскольку этот объект является изменчивым объектом (в данном случае - dict), изменение t также изменяет d, поскольку существует только один объект. Хотя это необходимо здесь, если по какой-либо причине в другом коде вы хотите сделать копию изменчивого объекта и работать с копией без изменения оригинала, вам необходимо указать import copy и использовать copy.deepcopy().

Во втором случае конструктор defaultdict() ожидает в качестве своего первого аргумента вызываемого, который не принимает аргументы и возвращает значение по умолчанию. Тем не менее, для этого случая это возвращаемое значение должно быть другим значением defaultdict с вызываемым возвратом другого defaultdict с вызываемым возвращаемым еще одним ... и т. Д. Это бесконечная рекурсия.

Таким образом, для этого кода нет эквивалента defaultdict. Вместо этого исходная версия с setdefault и простой dicts, вероятно, является лучшим и наиболее Pythonic способом сделать это.

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 означает ячейку памяти Т- и d оба одинаковы.
поэтому, когда код t = t.setdefault(char,{}) excecutes first t.setdefault(char,{}) выполняет и изменяет содержимое в ячейке памяти t, затем он возвращает содержимое, а затем назначает новую ячейку памяти для имени t и присваивает ей возвращаемое значение. Память местоположения t и d - это то же самое, что и причина, на которую влияет d.

Смежные вопросы