2013-10-04 4 views
58

Есть ли способ сделать defaultdict также стандартным для defaultdict? IOW, если я:defaultdict defaultdict, inested

x = defaultdict(...stuff...) 
x[0][1][0] 
{} 

Вот что я хочу. Вероятно, я просто воспользуюсь шаблоном пучков, но когда я понял, что не знаю, как это сделать, меня это заинтересовало.

Итак, я могу сделать:

x = defaultdict(defaultdict) 

, но это только один уровень:

x[0] 
{} 
x[0][0] 
KeyError: 0 

Есть рецепты, которые могут это сделать. Но можно ли это просто использовать обычные аргументы defaultdict?

Обратите внимание, что кто-то отметил это как дубликат Python: defaultdict of defaultdict?, но это не тот же вопрос ... этот вопрос состоял в том, как сделать двухуровневый defaultdict; это то, как сделать рекурсивный defaultdict с бесконечным уровнем.

+0

Возможный дубликат [Python: defaultdict из defaultdict] (http://stackoverflow.com/questions/5029934/python-defaultdict-of-defaultdict) – malioboro

+0

Не совсем ... добавила информацию к вопросу, чтобы указать, почему. Хотя это полезный вопрос. –

ответ

88

Для произвольного числа уровней:

def rec_dd(): 
    return defaultdict(rec_dd) 

>>> x = rec_dd() 
>>> x['a']['b']['c']['d'] 
defaultdict(<function rec_dd at 0x7f0dcef81500>, {}) 
>>> print json.dumps(x) 
{"a": {"b": {"c": {"d": {}}}}} 

Конечно, вы также можете сделать это с лямбда, но я считаю, лямбды быть менее читаемым. В любом случае это будет выглядеть следующим образом:

rec_dd = lambda: defaultdict(rec_dd) 
+1

Действительно прекрасный пример, спасибо. Не могли бы вы распространить его на случай, что данные загружаются из json в defaultdict defaultdict? –

+0

Одно примечание. Если вы пытаетесь использовать этот код, а травление lambda не будет работать. –

25

Существует отличный трюк для выполнения, что:

tree = lambda: defaultdict(tree) 

Тогда вы можете создать свой x с x = tree().

17

Аналогично решения BrenBarn, но не содержит имя переменной tree дважды, поэтому он работает даже после изменения переменного словаря:

tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a)))) 

Затем вы можете создать каждый новый x с x = tree().


Для версии def, мы можем использовать функцию закрытия возможности для защиты структуры данных от дефекта, где существующие экземпляры перестают работать, если имя tree является отбоя. Это выглядит следующим образом:

from collections import defaultdict 

def tree(): 
    def the_tree(): 
     return defaultdict(the_tree) 
    return the_tree() 
+4

Мне нужно подумать об этом (это немного сложнее). но я думаю, что ваша точка в том, что если x = tree(), но тогда кто-то приходит позже и делает tree = None, этот будет работать, и это не будет? –

+3

Правильно, это моя точка зрения. – pts

75

Другие ответы здесь сказать вам, как создать defaultdict, который содержит «бесконечно много» defaultdict, но они не в состоянии решить, что я думаю, что, возможно, ваши первоначальные потребности, которая должна была просто двухстрочный defaultdict.

Возможно, вы искали:

defaultdict(lambda: defaultdict(dict)) 

Причину, почему вы, возможно, предпочтете эту конструкцию:

  • Это более явное, чем рекурсивное решение, и поэтому, вероятно, более понятный для читатель.
  • Это позволяет «лист» из defaultdict быть что-то иное, чем словарь, например ,: defaultdict(lambda: defaultdict(list)) или defaultdict(lambda: defaultdict(set))
+2

defaultdict (lambda: defaultdict (list)) Правильная форма? –

+0

Ooops, да, форма 'lambda' правильная - потому что' defaultdict (something) 'возвращает объект, подобный словарю, но' defaultdict' ожидает вызываемого! Спасибо! –

+0

Это было отмечено как возможный дубликат другого вопроса ... но это был не мой первоначальный вопрос. Я знал, как создать двухуровневый defaultdict; я не знал, как сделать это рекурсивным. Этот ответ, по сути, похож на http://stackoverflow.com/questions/5029934/python-defaultdict-of-defaultdict –

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