2017-08-31 156 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没有深度限制。