Подтвердить что ты не робот

Вложенный defaultdict из defaultdict

Есть ли способ сделать defaultdict также по умолчанию для defaultdict? (т.е. рекурсивный defaultdict бесконечного уровня?)

Я хочу быть в состоянии сделать:

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

Итак, я могу сделать x = defaultdict(defaultdict), но это только второй уровень:

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

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

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

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

4b9b3361

Ответ 1

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

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)

Ответ 2

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

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

defaultdict(lambda: defaultdict(dict))

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

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

Ответ 3

Для этого есть отличный трюк:

tree = lambda: defaultdict(tree)

Затем вы можете создать x с помощью x = tree().

Ответ 4

Подобно решению 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()

Ответ 5

Я также хотел бы предложить больше реализации в стиле ООП, которая поддерживает бесконечное вложение, а также правильно отформатированный repr.

class NestedDefaultDict(defaultdict):
    def __init__(self):
        super(NestedDefaultDict, self).__init__(NestedDefaultDict)

    def __repr__(self):
        return repr(dict(self))

Использование:

my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']

print(my_dict)  # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}