【发布时间】:2020-07-18 00:26:17
【问题描述】:
我有一个 yaml 文件,我将其转换为嵌套字典变量。字典被传递到一个 python 函数 (render_yaml_dict()) 中,该函数接受具有未格式化值的嵌套字典并返回具有格式化值的嵌套字典。我正在使用递归来解析嵌套字典和 jinja 格式,以使用兄弟值格式化字符串值。
这是函数的输入嵌套字典:
{'y': {'a1': 'foo', 'a2': 'bar{{a1}}', 'a3': {'b1': 'bird', 'b2': 'red{{b1}}'}},
'x': {'c1': 'turtle'}}
这是函数的预期返回值:
{'y': {'a1': 'foo', 'a2': 'barfoo', 'a3': {'b1': 'bird', 'b2': 'redbird'}},
'x': {'c1': 'turtle'}}
这是函数的实际返回值:
{'y': {'a1': 'foo', 'a2': 'barfoo', 'a3': {'b1': 'bird', 'b2': 'red{{b1}}'}},
'x': {'c1': 'turtle'}}
问题
从上面的实际返回值可以看出,该函数没有格式化b2值,而是输出'red{{b1}}'的原始未格式化值。 a3 是 b2 的直接父级,它被添加到 parent_list (应该会发生),但问题是 a3 也被添加到 visited 列表中,而没有任何明确的代码这样做(请参阅下面的render_yaml_dict() 函数中的### 问题:###)。因此,当render_yaml_dict() 检查 parent_list 是否在访问列表中时,'b2': 'red{{b1}}' 不会被传递到 jinja 模板代码中。如果您运行代码块,显示此问题的打印语句如下。
stack list
[[('a1', 'foo'), ('a2', 'bar{{a1}}'), ('a3', {'b1': 'bird', 'b2': 'red{{b1}}'})]]
immediate_parent
a3
visited BEFORE appending immediate parent to parent_list
[['x'], ['y']]
parent_list BEFORE appending immediate parent to parent_list
['y']
parent_list AFTER appending immediate parent to parent_list
['y', 'a3']
visited AFTER appending immediate parent to parent_list
[['x'], ['y', 'a3']]
这是我目前拥有的 python 函数:
from functools import reduce
import operator
from yaml import safe_load
from jinja2 import Template, Undefined
def get_by_path(root, items):
"""Access a nested object in root by item sequence."""
return reduce(operator.getitem, items, root)
def set_by_path(root, items, value):
"""Set a value in a nested object in root by item sequence."""
get_by_path(root, items[:-1])[items[-1]] = value
def render_yaml_dict(nested_dict):
# used for jinja formatting
class NullUndefined(Undefined):
def __getattr__(self, key):
return ''
stack = list(nested_dict.items())
visited = []
parent_list = []
while stack:
#dict used to store non nested key : value pairs
not_dict = {}
#dict used to store key : nested value pairs
is_dict = {}
#reset is_stack to false for every stack iteration
is_stack = False
immediate_parent = None
print('\n \n stack list')
print(stack)
#pop last value in stack list
current_stack = stack.pop()
#if the current_stack is a tuple, put in list before converting to dict
if isinstance(current_stack, tuple):
stack_dict = dict([current_stack])
else:
stack_dict = dict(current_stack)
#ordered list of parent keys that are parents of nested values
for key,value in stack_dict.items():
#if value is a nested dictionary and the list of upstream parents haven't been chronologically visited
if isinstance(value, dict) and parent_list not in visited:
immediate_parent = key
add_to_stack = list(value.items())
is_dict.update({key:value})
is_stack = True
#if value isn't a nested dictionary and the list of upstream parents haven't been chronologically visited
elif isinstance(value, dict) == False and parent_list not in visited:
not_dict.update({key:value})
else:
continue
# if key value pairs exist in not_dict dictionary
if not_dict:
#create duplicate dictionary of values to use for templating
t = Template(str(not_dict), undefined=NullUndefined)
c = safe_load(t.render())
#format dictionary of values with template dictionary
formatted_dict = safe_load(t.render(c))
# add is_dict to formatted_dict because set_by_path() updates inplace
formatted_dict = {**formatted_dict, **is_dict}
# update nested_dict with formatted_dict
set_by_path(nested_dict, parent_list, formatted_dict)
# if there is only one parent node
if len(parent_list) == 1:
#append parent_list to visited
visited.append(parent_list)
elif len(parent_list) > 1:
#append parent_list by don't include immediate parent
visited.append(parent_list[:-1])
else:
pass
#if there is atleast one key:nested value pair in current_stack
if is_stack == True:
### PROBLEM:### The immediate_parent is added to the visited list even though there's no code that explicitly does
print('\nimmediate_parent')
print(immediate_parent)
print('visited BEFORE appending immediate parent to parent_list')
print(visited)
print('parent_list BEFORE appending immediate parent to parent_list')
print(parent_list)
#add latest parent to parent_list
parent_list.append(immediate_parent)
print('parent_list AFTER appending immediate parent to parent_list')
print(parent_list)
print('visited AFTER appending immediate parent to parent_list')
print(visited)
#add latest key: nested value pair to stack
if len(add_to_stack) > 1:
stack.append(add_to_stack)
else:
stack.extend(add_to_stack)
#if there are no key: nested value pairs in current_stack, reset parent_list
else:
if len(parent_list) > 1:
del parent_list[-1]
else:
parent_list = []
return nested_dict
config = {'y': {'a1': 'foo', 'a2': 'bar{{a1}}', 'a3': {'b1': 'bird', 'b2': 'red{{b1}}'}},
'x': {'c1': 'turtle'}}
render_yaml_dict(config)
【问题讨论】:
-
你为什么不使用像
pyyaml这样的库? -
@bigbounty 我目前正在查看
pyyaml文档。pyyaml是否有您必须传递的方法或参数以使用兄弟值格式化 yaml 值? -
为什么不看看这个库 - github.com/fabiocaccamo/python-benedict?
-
@bigbounty 在 python benedict 中这个问题有什么用处?
-
访问列表中没有添加任何内容。它之前包含两个元素,之后包含两个元素。修改后第二个元素的值发生了变化。
标签: python-3.x recursion yaml