2014-10-06 140 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