2017-08-31 157 views
6

我想要什麼

從YAML配置我得到一個Python字典,看起來像這樣:繼承在配置字典

conf = { 
    'cc0': { 
     'subselect': 
      {'roi_spectra': [0], 'roi_x_pixel_spec': 'slice(400, 1200)'}, 
     'spec': 
      {'subselect': {'x_property': 'wavenumber'}}, 

     'trace': 
      {'subselect': {'something': 'jaja', 'roi_spectra': [1, 2]}} 
    } 
} 

正如你所看到的關鍵字是「再選擇」通用於所有的子級其價值總是一個字典,但其存在是可選的。嵌套量可能會改變。 我在尋找一個功能,可以讓我做到以下幾點:

# desired function that uses recursion I belive. 
collect_config(conf, 'trace', 'subselect') 

其中「追蹤」是類型的字典字典的關鍵,可能有一個「子查詢」字典的價值。

和它應該返回

{'subselect':{ 
    'something': 'jaja', 
    'roi_spectra': [1, 2], 
    'roi_x_pixel_spec': 
    'slice(400, 1200)' 
} 

,或者如果我問

collect_config(conf, "spec", "subselect") 

它應該返回

{'subselect':{ 
    'roi_spectra': [0], 
    'roi_x_pixel_spec': 
    'slice(400, 1200)', 
    'x_property': 'wavenumber' 
} 

我基本上要什麼,是傳遞值的方法從頂級到低級的一個關鍵點,並且具有較低級別的覆蓋e頂級值。 很像繼承類,但與字典。

所以我需要一個遍歷字典的函數,找到所需鍵的路徑(這裏是「trace」或「spec」)並用更高級別的值填充它的值(這裏是「subselect」),但只有當heigher水平值不存在。

一個蹩腳的解決方案

目前,我有一種實現的,看起來如下。

# This traverses the dict and gives me the path to get there as a list. 
def traverse(dic, path=None): 
    if not path: 
     path=[] 
    if isinstance(dic, dict): 
     for x in dic.keys(): 
      local_path = path[:] 
      local_path.append(x) 
      for b in traverse(dic[x], local_path): 
       yield b 
    else: 
     yield path, dic 

# Traverses through config and searches for the property(keyword) prop. 
# higher levels will update the return 
# once we reached the level of the name (max_depth) only 
# the path with name in it is of interes. All other paths are to 
# be ignored. 
def collect_config(config, name, prop, max_depth): 
    ret = {} 
    for x in traverse(config): 
     path = x[0] 
     kwg = path[-1] 
     value = x[1] 
     current_depth = len(path) 
     # We only care about the given property. 
     if prop not in path: 
      continue 
     if current_depth < max_depth or (current_depth == max_depth and name in path): 
      ret.update({kwg: value}) 
    return ret 

,然後我可以

叫它
read_config(conf, "trace", 'subselect', 4) 

,並得到

{'roi_spectra': [0], 
'roi_x_pixel_spec': 'slice(400, 1200)', 
'something': 'jaja'} 

更新

jdehesa是幾乎沒有,但我也可以有一個配置,看起來像:

conf = { 
    'subselect': {'test': 'jaja'} 
    'bg0': { 
     'subselect': {'roi_spectra': [0, 1, 2]}}, 
    'bg1': { 
     'subselect': {'test': 'nene'}}, 
} 

collect_config(conf, 'bg0', 'subselect') 

{'roi_spectra': [0, 1, 2]} 

,而不是

{'roi_spectra': [0, 1, 2], 'test': 'jaja'} 
+0

所以基本上你想要由最後一個參數命名的字典(提供詳細信息),用從第二個參數加上最後一個參數形成的路徑加載的覆蓋更新。 –

+0

函數應該如何找出頂級'conf'字典中的關鍵字?在你的例子中,只有一個鍵「cc0」。如果總是隻有一個鍵,那有什麼意義?如果有時不止一個,應該採用哪一個? –

+0

你可以檢查,如果這可以幫助你(https://stackoverflow.com/questions/9807634/find-all-occurrences-of-a-key-in-nested-python-dictionaries-and-lists) –

回答

0

這是我的看法:

def collect_config(conf, key, prop, max_depth=-1): 
    prop_val = conf.get(prop, {}).copy() 
    if key in conf: 
     prop_val.update(conf[key].get(prop, {})) 
     return prop_val 
    if max_depth == 0: 
     return None 
    for k, v in conf.items(): 
     if not isinstance(v, dict): 
      continue 
     prop_subval = collect_config(v, key, prop, max_depth - 1) 
     if prop_subval is not None: 
      prop_val.update(prop_subval) 
      return prop_val 
    return None 

conf = { 
    'cc0': { 
     'subselect': 
      {'roi_spectra': [0], 'roi_x_pixel_spec': 'slice(400, 1200)'}, 
     'spec': 
      {'subselect': {'x_property': 'wavenumber'}}, 

     'trace': 
      {'subselect': {'something': 'jaja', 'roi_spectra': [1, 2]}} 
    } 
} 
print(collect_config(conf, "trace", 'subselect', 4)) 
>>> {'roi_x_pixel_spec': 'slice(400, 1200)', 
    'roi_spectra': [1, 2], 
    'something': 'jaja'} 

conf = { 
    'subselect': {'test': 'jaja'}, 
    'bg0': { 
     'subselect': {'roi_spectra': [0, 1, 2]}}, 
    'bg1': { 
     'subselect': {'test': 'nene'}}, 
} 
print(collect_config(conf, 'bg0', 'subselect')) 
>>> {'roi_spectra': [0, 1, 2], 'test': 'jaja'} 

離開max_depth作爲-1會遍歷conf沒有深度限制。