Ответ 1
Вы можете сделать это, даже не определяя класс:
from collections import defaultdict
nested_dict = lambda: defaultdict(nested_dict)
nest = nested_dict()
nest[0][1][2][3][4][5] = 6
У меня есть большой список вроде:
[A][B1][C1]=1
[A][B1][C2]=2
[A][B2]=3
[D][E][F][G]=4
Я хочу построить многоуровневый dict, например:
A
--B1
-----C1=1
-----C2=1
--B2=3
D
--E
----F
------G=4
Я знаю, что если я использую рекурсивный defaultdict, я могу написать table[A][B1][C1]=1
, table[A][B2]=2
, но это работает только в том случае, если я жестко кодирую эти инструкции insert.
Во время разбора списка я не знаю, сколько [] мне нужно позвонить table[key1][key2][...]
.
Вы можете сделать это, даже не определяя класс:
from collections import defaultdict
nested_dict = lambda: defaultdict(nested_dict)
nest = nested_dict()
nest[0][1][2][3][4][5] = 6
В вашем примере сказано, что на любом уровне может быть значение, а также словарь подэлементов. Это называется tree, и для них доступно множество реализаций. Это одно:
from collections import defaultdict
class Tree(defaultdict):
def __init__(self, value=None):
super(Tree, self).__init__(Tree)
self.value = value
root = Tree()
root.value = 1
root['a']['b'].value = 3
print root.value
print root['a']['b'].value
print root['c']['d']['f'].value
Выходы:
1
3
None
Вы можете сделать что-то подобное, записав ввод в JSON и используя json.load
, чтобы прочитать его как структуру вложенных словарей.
Я бы сделал это с подклассом dict
, который определяет __missing__
:
>>> class NestedDict(dict):
... def __missing__(self, key):
... self[key] = NestedDict()
... return self[key]
...
>>> table = NestedDict()
>>> table['A']['B1']['C1'] = 1
>>> table
{'A': {'B1': {'C1': 1}}}
Вы не можете сделать это напрямую с defaultdict, потому что defaultdict ожидает функцию factory во время инициализации, но во время инициализации нет способ описать один и тот же defaultdict. Вышеупомянутая конструкция делает то же самое, что и default dict, но поскольку это именованный класс (NestedDict), он может ссылаться на себя, поскольку встречаются недостающие ключи. Также возможно подклассы defaultdict и переопределить __init__
.
Я думаю, что самая простая реализация рекурсивного словаря - это. Только конечные узлы могут содержать значения.
# Define recursive dictionary
from collections import defaultdict
tree = lambda: defaultdict(tree)
Использование:
# Create instance
mydict = tree()
mydict['a'] = 1
mydict['b']['a'] = 2
mydict['c']
mydict['d']['a']['b'] = 0
# Print
import prettyprint
prettyprint.pp(mydict)
Выход:
{
"a": 1,
"b": {
"a": 1
},
"c": {},
"d": {
"a": {
"b": 0
}
}
}
Это эквивалентно приведенному выше, но избегая обозначения лямбда. Может быть, легче читать?
def dict_factory():
return defaultdict(dict_factory)
your_dict = dict_factory()
Также - из комментариев - если вы хотите обновить из существующего dict, вы можете просто вызвать
your_dict[0][1][2].update({"some_key":"some_value"})
Чтобы добавить значения в dict.
Dan O'Huiginn опубликовал очень хорошее решение в своем журнале в 2010 году:
http://ohuiginn.net/mt/2010/07/nested_dictionaries_in_python.html
>>> class NestedDict(dict):
... def __getitem__(self, key):
... if key in self: return self.get(key)
... return self.setdefault(key, NestedDict())
>>> eggs = NestedDict()
>>> eggs[1][2][3][4][5]
{}
>>> eggs
{1: {2: {3: {4: {5: {}}}}}}
Несколько другая возможность, которая позволяет регулярную инициализацию словаря:
from collections import defaultdict
def superdict(arg=()):
update = lambda obj, arg: obj.update(arg) or obj
return update(defaultdict(superdict), arg)
Пример:
>>> d = {"a":1}
>>> sd = superdict(d)
>>> sd["b"]["c"] = 2
Чтобы добавить к @Hugo
Чтобы иметь максимальную глубину:
l=lambda x:defaultdict(lambda:l(x-1)) if x>0 else defaultdict(dict)
arr = l(2)
Вы можете достичь этого с помощью рекурсивного defaultdict
.
from collections import defaultdict
def tree():
def the_tree():
return defaultdict(the_tree)
return the_tree()
Важно защитить заводское имя по умолчанию, the_tree
здесь, в замыкании ("частная" область видимости локальной функции). Избегайте использования однолинейной lambda
версии, которая содержит ошибки из-за поздних закрытий привязки Python, и вместо этого реализуйте ее с помощью def
.
Принятый ответ, использующий лямбду, имеет недостаток, когда экземпляры должны полагаться на имя nested_dict
существующее во внешней области видимости. Если по какой-либо причине фабричное имя не может быть разрешено (например, оно было восстановлено или удалено), то ранее существующие экземпляры также будут слегка повреждены:
>>> nested_dict = lambda: defaultdict(nested_dict)
>>> nest = nested_dict()
>>> nest[0][1][2][3][4][6] = 7
>>> del nested_dict
>>> nest[8][9] = 10
# NameError: name 'nested_dict' is not defined
У вас table['A']=defaultdict()
.