2014-10-06 136 views
3

有没有什么办法来动态创建丢失的键如果我想要在子字典中设置一个变量。递归字典创建python

基本上我想创建任何丢失的键并设置我的值。

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

目前,我这样做,但它的凌乱:

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'] = [] 

有没有办法中的一个或两行而不是这样做呢?

回答

3

这是不容易的递归的事:

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 

和测试案例:

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

如果你想拥有它,所以你并不需要的功能,你可以使用下面的defaultdict工厂允许你任意深入地嵌套:

from collections import defaultdict 

defaultdict_factory = lambda : defaultdict(defaultdict_factory) 

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

递归'defaultdict'厂是一个有趣的想法!我没有想过这样做。 – DaoWen 2014-10-06 19:52:45

+1

当OP在最后一个深度想要一个“列表”时失败。 – o11c 2014-10-06 20:18:27

+0

@ o11c - 不是。这意味着OP需要明确地设置它。 'd''foo'] ['bar'] ['baz'] = []'或'd ['foo'] ['bar'] = defaultdict(list)'OP必须为其他任何人答案以及... – mgilson 2014-10-06 20:41:43

1

使用collections.defaultdict

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

将'defaultdict(list)'放在结束 – matsjoyce 2014-10-06 19:26:29

+0

@matsjoyce我认为这是更明显的说法,因为大多数人不认为'list'是可调用的 – o11c 2014-10-06 19:28:03

+2

我认为那些不认为'list'可调用的人不太了解'lambda但是,你认为有什么可以帮助OP – matsjoyce 2014-10-06 19:30:45

0

有一个近距离观察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})})}) 
+0

我发布了这个之后,我看到了一些其他的答案提到defaultdict OP,你决定这是否有助于理解defaultdict,否则我会删除它 – 2014-10-06 19:31:14

1

我碰到的,在过去类似的问题。我发现defaultdict对我来说是正确的答案 - 但是编写超长的定义(比如@ o11c的答案或@ Apero的答案)是不好的。以下是我想出了替代:

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)) 

这将创建嵌套字典levels字典。所以如果你有levels = 3,那么你需要3个键来访问底层的值。第二个参数是一个用来创建底层值的函数。类似listlambda: 0或甚至dict会运作良好。

下面是一个使用“自动”键,带4 levels,并且list作为默认函数的例子:

>>> 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']})})})}) 

我认为这基本上你想要什么,你的问题的情况下。你4“级别”是开关名,邻居的名字,本地,&港口 - 和它看起来像你想list在最下层的存储您的端口。

使用2 levelslambda: 0作为默认又如:

>>> 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})}) 
+0

很酷的答案,虽然IMO正确的事情会实际上是为中层引入额外的课程,以便每个课程只包含一个深度的defaultdict的 – o11c 2014-10-06 20:19:47

+0

@ o11c - 这是一个有趣的观点。我认为添加额外的类只是没有意义的复杂性,因为实际上并没有为任何类使用类,而是包装一个'defaultdict'。另一方面,我认为用类实例完全替换一些'defaultdict'的级别是有益的。例如,'local'和'ports'看起来更像类字段名称而不是动态字典条目,所以将它们替换为具有'local'和'ports'字段的类可能会更好。也许这就是你的意思。 – DaoWen 2014-10-07 15:59:25

+0

特别是我想''portdict [switchname]'显然应该返回'Switch','switch [neighborname]'应该返回'Neighbor' – o11c 2014-10-07 19:57:17