Предотвращение ошибок хранения ключей во вложенном словаре (Python)
Вступление
Следующий словарь имеет три уровня ключей, а затем значение.
d = {
1:{
'A':{
'i': 100,
'ii': 200
},
'B':{
'i': 300
}
},
2:{
'A':{
'ii': 500
}
}
}
Примеры, которые необходимо добавить.
d[1]['B']['ii'] = 600 # OK
d[2]['C']['iii'] = 700 # Keyerror on 'C'
d[3]['D']['iv'] = 800 # Keyerror on 3
Постановка задачи
Я хотел создать код, который бы создавал необходимые вложенные ключи и избегал ошибок ключей.
Решение 1
Первое решение, которое я придумал, было:
def NewEntry_1(d, lv1, lv2, lv3, value):
if lv1 in d:
if lv2 in d:
d[lv1][lv2][lv3] = value
else:
d[lv1][lv2] = {lv3: value}
else:
d[lv1] = {lv2: {lv3: value}}
Кажется законным, но встраивание этого в куски кода порядка заставило его ошеломить. Я исследовал Stackru для других решений и читал функции get() и setdefault().
Решение 2
Существует множество материалов о get() и setdefault(), но не так много о вложенных словарях. В конечном итоге я смог придумать:
def NewEntry_2(d, lv1, lv2, lv3, value):
return d.setdefault(lv1, {}).setdefault(lv2,{}).setdefault(lv3, value)
Это одна строка кода, поэтому нет необходимости делать это функцией. Легко модифицируется для включения операций:
d[lv1][lv2][lv3] = d.setdefault(lv1, {}).setdefault(lv2,{}).setdefault(lv3, 0) + value
Кажется идеальным?
Вопрос
При добавлении большого количества записей и выполнении многих модификаций вариант 2 лучше, чем вариант 1? Или я должен определить функцию 1 и вызвать ее? Ответы, которые я ищу, должны учитывать скорость и / или вероятность ошибок.
Примеры
NewEntry_1(d, 1, 'B', 'ii', 600)
# output = {1: {'A': {'i': 100, 'ii': 200}, 'B': {'i': 300, 'ii': 600}}, 2: {'A': {'ii': 500}}}
NewEntry_1(d, 2, 'C', 'iii', 700)
# output = {1: {'A': {'i': 100, 'ii': 200}, 'B': {'i': 300, 'ii': 600}}, 2: {'A': {'ii': 500}, 'C': {'iii': 700}}}
NewEntry_1(d, 3, 'D', 'iv', 800)
# output = {1: {'A': {'i': 100, 'ii': 200}, 'B': {'i': 300, 'ii': 600}}, 2: {'A': {'ii': 500}, 'C': {'iii': 700}}, 3: {'D': {'iv': 800}}}
Больше фона
Я бизнес-аналитик, исследующий использование Python для создания Graph DB, который помог бы мне с очень специфическим анализом. Структура словаря используется для описания влияния одного узла на одного из его соседей:
- lv1 - это Узел Из
- lv2 - это узел To
- lv3 - это итерация
- значение Влияние (в%)
На первой итерации Node 1 оказывает непосредственное влияние на Node 2. На второй итерации Node 1 влияет на все Nodes, на которые влияет Node 2.
Мне известны пакеты, которые могут помочь мне в этом (networkx), но я пытаюсь понять Python/GraphDB, прежде чем я хочу начать их использовать.
1 ответ
Что касается вложенных словарей, вы должны взглянуть на defaultdict
, Его использование избавит вас от накладных расходов при вызове функций. Вложенные defaultdict
строительство курортов в lambda
функции для их заводов по умолчанию:
d = defaultdict(lambda: defaultdict(lambda: defaultdict(int))) # new, shiny, empty
d[1]['B']['ii'] = 600 # OK
d[2]['C']['iii'] = 700 # OK
d[3]['D']['iv'] = 800 # OK
Обновление: полезный трюк, чтобы знать, чтобы создать глубоко вложенный defaultdict
является следующим:
def tree():
return defaultdict(tree)
d = tree()
# now any depth is possible
# d[1][2][3][4][5][6][7][8] = 9