Удобный способ обработки глубоко вложенного словаря в Python - PullRequest
0 голосов
/ 25 апреля 2018

У меня есть глубоко вложенный словарь в python, занимающий много места. Есть ли способ сократить что-то вроде этого

master_dictionary['sub_categories'][sub_cat_name]['attributes'][attribute_name]['special_type']['nested_children'][child_cat_name][color] = blue

к этому, например

nested_child_info[color] = blue

И все-таки нужно ли редактировать словарь? Я надеюсь, что это имеет смысл.

Ответы [ 6 ]

0 голосов
/ 25 апреля 2018

Похоже на @fferri. Вы всегда должны будете указывать элементы в списке long . Получите ссылку на окончательный вариант, используя reduce и getitem:

from functools import reduce
from operator import getitem

d = {1:{2:{3:{4:5}}}}

foo = 2
items = [1,foo,3]
result = d
info = reduce(getitem, items, d)


>>> info[4]
5
>>> d
{1: {2: {3: {4: 5}}}}
>>> info[4] = 99
>>> d
{1: {2: {3: {4: 99}}}}

Я также играл с классом, но у него, похоже, нет особых преимуществ - за исключением того, что вы можете настроить исключение ключевой ошибки, чтобы в сообщении об ошибке указывалось, какой ключ на какой глубине отсутствует.

class Drilldown:
    def __init__(self, d, path):
        #self.final = reduce(getitem, path, d)
        self.final = d
        for i, item in enumerate(path, 1):
            try:
                self.final = self.final[item]
            except KeyError as e:
                msg = ''.join('[{}]' for _ in range(i))
                msg = msg.format(*path[:i])
                msg = 'The last key in the path "{}" does not exist'.format(msg)
                e.args = [msg]
                raise
    def __call__(self, item):
        return self.final[item]
    def __setitem__(self, item, value):
        self.final[item] = value
    def __getitem__(self, item):
        return self.final[item]
    def __str__(self):
        return str(self.final)
    def __repr__(self):
        return repr(self.final)

>>> z = 19
>>> items = [1,2,z]
>>> q = Drilldown(d,items)
Traceback (most recent call last):
  File "<pyshell#68>", line 1, in <module>
    q = Drilldown(d,items)
  File "C:\pyProjects33\tmp.py", line 32, in __init__
    self.final = self.final[item]
KeyError: 'The last key in the path "[1][2][19]" does not exist'

>>> 
>>> #normal usage
>>> items = [1,2,3]
>>> q = Drilldown(d,items)
>>> d
{1: {2: {3: {4: 5}}}}
>>> q
{4: 5}
>>> q(4)
5
>>> q[4]
5
>>> q[4] += 20
>>> q
{4: 25}
>>> d
{1: {2: {3: {4: 25}}}}
>>> q['foo'] = '99'
>>> q
{4: 25, 'foo': '99'}
>>> d
{1: {2: {3: {4: 25, 'foo': '99'}}}}
>>> 
0 голосов
/ 25 апреля 2018

Если у вас есть «фиксированные» клавиши, вы всегда можете создать функцию:

Рассмотрим этот пример:

d = dict(a=dict(sub_categories=dict(b=1)))

def changevalue(value, lvl1, lvl2):
    d[lvl1]['sub_categories'][lvl2] = value

changevalue(2,'a','b')

print(d)

#{'a': {'sub_categories': {'b': 2}}}

В вашем случае вы хотели бы поднять:

[sub_cat_name], [attribute_name], [child_cat_name], [color] ... возможно

0 голосов
/ 25 апреля 2018

Да, вы можете.

>>> dict1 = {'foo':{'bar':{'baz':0}}}
>>> dict2 = dict1['foo']['bar']
>>> dict2['baz'] = 1
>>> dict1
{'foo': {'bar': {'baz': 1}}} # dict1 has been modified
0 голосов
/ 25 апреля 2018

Поскольку словари являются изменяемыми, это на самом деле будет происходить именно так, как вы ожидаете:

>>> test = {'outer': 'thing', 'inner': {'thing': 'im the inner thing'}}
>>> inner_thing = test['inner']
>>> inner_thing
{'thing': 'im the inner thing'}
>>> inner_thing['thing'] = 'im something new'
>>> inner_thing
{'thing': 'im something new'}
>>> test
{'outer': 'thing', 'inner': {'thing': 'im something new'}}

Это потому, что изменяемые объекты передаются по ссылке в python, а не как копия (множество отличныхстатьи на эту тему, так что не буду вдаваться в подробности).

Однако стоит отметить, что вы, возможно, на самом деле не захотите изменять исходный dict, так как это может иметь нежелательные последствия для другого кода, использующего эту переменную (этозависит от вашей кодовой базы).В этой ситуации я обычно делаю копию данных, которые необходимо изменить, чтобы избежать побочных эффектов:

>>> from copy import deepcopy
>>> test = {'outer': 'thing', 'inner': {'thing': 'im the inner thing'}}
>>> new_test = deepcopy(test)
>>> inner_thing = test['inner']
>>> inner_thing['thing'] = 'im something new'
>>> test
{'outer': 'thing', 'inner': {'thing': 'im something new'}}
>>> new_test
{'outer': 'thing', 'inner': {'thing': 'im the inner thing'}}
0 голосов
/ 25 апреля 2018

Не могли бы вы сделать что-то подобное?

thing = {1: {2: {3: {4: {'hello': 'world'}}}}}
a = thing[1]
b = a[2]
c = b[3]
d = c[4]
print(d) # {'hello': 'world'}
0 голосов
/ 25 апреля 2018
nested_child_info = master_dictionary['sub_categories'][sub_cat_name]['attributes'][attribute_name]['special_type']['nested_children'][child_cat_name]
nested_child_info[color] = blue

nested_child_info является ссылкой , поэтому изменение его содержимого приведет к изменению содержимого master_dictionary.

...