为简化复杂配置,所有内容都存储在字典中。 配置的某些部分会重复多次,并且很容易复制和粘贴它们,即使它们经常更改。
为了避免这种重复,在词典中有一些键(以' _'开头)将通用参数组合在一起:即
{
....
"_common_settings": {
val1: 0,
val2: 1,
}
....
}
其他地方可以通过以下方式调用这组设置:
{
...
"extra": "_common_settings"
...
}
技巧由extra
键完成:每次找到它时,它都会被其定义所取代。它仅作为字典键出现,如果是列表元素则不进行管理。
整个配置可以是字典和列表的混合,字典中的字典或任何其他组合和任何深度。
这是初始配置的简短示例:
initial_cfg = {
# -------------------------------------------------------------------
# common definitions
"_other_common_params": {
"fields": "all",
"polling": 5
},
"_extra_params100": {
"ip": "10.1.0.1",
"name": "db100",
"extra": "_other_common_params"
},
"_extra_params200": {
"ip": "10.1.0.2",
"name": "db200",
"extra": "_other_common_params"
},
# -------------------------------------------------------------------
# page-specific definitions
"id101": {
"db": [
{ "id": "id101",
"table": "table101",
"extra": "_extra_params100"
}
]
},
"id201": {
"db": [
{ "id": "id201",
"table": "table201",
"extra": "_extra_params200"
},
{ "id": "id202",
"table": "table202",
"extra": "_extra_params200"
}
]
}
}
这是我想要获得的最终结果(以_开头的键被删除,因为它们没用):
final_cfg = {
"id101": {
"db": [
{ "id": "id101",
"table": "table101",
"ip": "10.1.0.1",
"name": "db100",
"fields": "all",
"polling": 5,
}
]
},
"id201": {
"db": [
{ "id": "id201",
"table": "table201",
"ip": "10.1.0.2",
"name": "db200",
"fields": "all",
"polling": 5
},
{ "id": "id202",
"table": "table202",
"ip": "10.1.0.2",
"name": "db200",
"fields": "all",
"polling": 5
}
]
}
}
我在使用这种递归替换方面遇到了麻烦,因为根据处理initial_config
的顺序,结果会发生变化,而extra
中会留下一些final_config
。
这是我现在使用的代码。递归不是我的面包,我无法修复它来管理initial_config的整个解码。
def init_pages_config():
global pages_config
def rebuild_pages_config(branch):
if type(branch) is list:
# in lists simple recursion, no substitution
for b in branch:
rebuild_pages_config(b)
elif type(branch) is dict:
# in dictionaries substitution, then recursion
if "extra" in branch:
key = branch["extra"]
del branch["extra"]
branch.update(pages_config[key])
for b in branch:
rebuild_pages_config(branch[b])
rebuild_pages_config(pages_config)
# remove the entries beginning with _
new_dict = {}
for pc in pages_config.keys():
if not pc.startswith("_"):
new_dict[pc] = pages_config[pc]
pages_config = new_dict
from pprint import pprint
pprint(pages_config)
答案 0 :(得分:2)
您需要首先处理您的_extra
映射。这些形成一个图表,您可以使用队列来确保它们完全展开:
from collections import deque
from itertools import chain
from functools import singledispatch
def expand_config(config):
extras = build_substitutions(config)
return substitute_recurse(config, extras)
def build_substitutions(config):
substitutions = {}
queue = deque((k, o.copy()) for k, o in config.items() if k[0] == '_')
while queue:
key, subst = queue.pop()
if 'extra' in subst:
if subst['extra'] not in substitutions:
# extra keys not yet processed
queue.appendleft((key, subst))
continue
subst.update(substitutions[subst.pop('extra')])
substitutions[key] = subst
return substitutions
@singledispatch
def substitute_recurse(obj, extras):
return obj
@substitute_recurse.register(dict)
def _dict(d, extras):
extra = extras.get(d.get('extra'), {})
return {k: substitute_recurse(v, extras)
for k, v in chain(d.items(), extra.items())
if k[0] != '_' and k != 'extra'}
@substitute_recurse.register(list)
def _list(l, extras):
return [substitute_recurse(v, extras) for v in l]
递归全部由using single dispatch处理,这使得每个类型的处理程序变得更加简单。