简体   繁体   中英

recursive dictionary creation python

is there any way to dynamically create missing keys if i want to want to set a variable in a subdictionary.

essentially I want to create any missing keys and set my value.

self.portdict[switchname][str(neighbor['name'])]['local']['ports'] = []

currently i'm doing it but its messy:

if not switchname in self.portdict:
    self.portdict[switchname] = {}
if not str(neighbor['name']) in self.portdict[switchname]:
    self.portdict[switchname][str(neighbor['name'])] = {}
if not 'local' in self.portdict[switchname][str(neighbor['name'])]:
    self.portdict[switchname][str(neighbor['name'])]['local'] = {}
if not 'ports' in self.portdict[switchname][str(neighbor['name'])]['local']:
    self.portdict[switchname][str(neighbor['name'])]['local']['ports'] = []

Is there any way to do this in one or two lines instead?

This is easier to do without recursion:

def set_by_path(dct, path, value):
    ipath = iter(path)
    p_last = next(ipath)
    try:
        while True:
            p_next = next(ipath)
            dct = dct.setdefault(p_last, {})
            p_last = p_next
    except StopIteration:
        dct[p_last] = value

And a test case:

d = {}
set_by_path(d, ['foo', 'bar', 'baz'], 'qux')
print d  # {'foo': {'bar': {'baz': 'qux'}}}

If you want to have it so you don't need a function, you can use the following defaultdict factory which allows you to nest things arbitrarily deeply:

from collections import defaultdict

defaultdict_factory = lambda : defaultdict(defaultdict_factory)

d = defaultdict_factory()
d['foo']['bar']['baz'] = 'qux'
print d

使用collections.defaultdict

self.portdict = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: []))))

I've run into a similar problem in the past. I found that defaultdict was the right answer for me—but writing the super long definitions (like the one in @o11c's answer or @Apero's answer) was no good. Here's what I came up with instead:

from collections import defaultdict
from functools import partial

def NestedDefaultDict(levels, baseFn):
    def NDD(lvl):
        return partial(defaultdict, NDD(lvl-1)) if lvl > 0 else baseFn
    return defaultdict(NDD(levels-1))

This creates a dictionary with levels of nested dictionaries. So if you have levels =3, then you need 3 keys to access the bottom-level value. The second argument is a function which is used to create the bottom-level values. Something like list or lambda: 0 or even dict would work well.

Here's an example of using the "automatic" keys with 4 levels , and list as the default function:

>>> x = NestedDefaultDict(4, list)
>>> x[1][2][3][4].append('hello')
>>> x
defaultdict(<functools.partial object at 0x10b5c22b8>, {1: defaultdict(<functools.partial object at 0x10b5c2260>, {2: defaultdict(<functools.partial object at 0x10b5c2208>, {3: defaultdict(<type 'list'>, {4: ['hello']})})})})

I think that's basically what you'd want for the case in your question. Your 4 "levels" are switch-name, neighbor-name, local, & ports —and it looks like you want a list at the bottom-level to store your ports.

Another example using 2 levels and lambda: 0 as the default:

>>> y = NestedDefaultDict(2, lambda: 0)
>>> y['foo']['bar'] += 7
>>> y['foo']['baz'] += 10
>>> y['foo']['bar'] += 1
>>> y
defaultdict(<functools.partial object at 0x1021f1310>, {'foo': defaultdict(<function <lambda> at 0x1021f3938>, {'baz': 10, 'bar': 8})})

Have a close look to collections.defaultdict:

from collections import defaultdict
foo = defaultdict(dict)
foo['bar'] = defaultdict(dict)
foo['bar']['baz'] = defaultdict(dict)
foo['bar']['baz']['aaa'] = 1
foo['bor'] = 0
foo['bir'] = defaultdict(list)
foo['bir']['biz'].append(1)
foo['bir']['biz'].append(2)

print foo
defaultdict(<type 'dict'>, {'bir': defaultdict(<type 'list'>, {'biz': [1, 2]}), 'bor': 0, 'bar': defaultdict(<type 'dict'>, {'baz': defaultdict(<type 'dict'>, {'aaa': 1})})})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM