简而言之,我使用 Yaml 文件作为我正在使用的某些管道/函数的参数配置。在Python中,这是一个嵌套字典,参数本身可以是数组/字典。迭代所有配置文件并搜索指定的值子集将很有帮助,例如
# toy example of all parameters a config file might have
- param_a: 1
- param_b:
- b1: 'a'
- b2: [1,2,3]
Run Code Online (Sandbox Code Playgroud)
# want all configs with these values
- param_a: 1
- param_b:
- b2: [1,2,3]
Run Code Online (Sandbox Code Playgroud)
当然,可以对每个嵌套字典进行递归,但我想知道是否有一个经过验证的真实解决方案,而不是重新发明轮子。
我看到了一些相关的问题(希望确认相同的词典)并且弹出Deep Diff 。然而,尚不清楚在测试子集时,DeepDiff 是否会返回所有丢失的键。想法?
现在我正在使用它并假设 yaml 已作为嵌套字典正确加载
def is_config_subset(truth, params):
'''
Arguments:
----------
truth (dict): dictionary of parameters to compare to
params (dict): dictionary of parameters to test
Returns:
----------
result (bool) whether or not `params` is a subset of `truth`
'''
if not type(truth) == type(params): return False
for key, val in params.items():
if key not in truth: return False
if type(val) is dict:
if not is_config_subset(truth[key], val):
return False
else:
if not truth[key] == val: return False
return True
print(is_config_subset({'a':1, 'b':2}, {'b':2}))
print(is_config_subset({'a':1, 'b':2}, {'b':2, 'c':3}))
print(is_config_subset({'a':1, 'b':2, 'c':[1,2,3]}, {'b':2, 'c':[1,2,3]}))
print(is_config_subset({'a':1, 'b':2, 'c':[1,2,3]}, {'b':2, 'c':[1,2]}))
print(is_config_subset({'a':1, 'b':2, 'c':[1,2,3]}, {'a':2, 'b':2}))
True
False
True
False
False
Run Code Online (Sandbox Code Playgroud)
这可能是一个简单的示例,并不适用于所有情况。
我认为最简单(也许也是最有效)的解决方案是定义您自己的递归辅助函数,例如is_subset. 由于 YAML 和 JSON 在格式上非常相似,因此您可以首先将 YAML 数据加载到 Python 类型(alist或dict),并将其传递给递归函数,该函数根据预定义的条件执行子集检查。
例如,这里有一个简单(大部分完整)的示例来帮助您入门。
def is_subset(superset, o):
"""Check if `o` is subset of `superset`"""
ss_type, o_type = type(superset), type(o)
if ss_type != o_type:
return False
# now we know that both `superset` and `o` are the same type
if o_type is dict:
for o_key, o_value in o.items():
ss_value = superset.get(o_key)
if not is_subset(ss_value, o_value):
return False
else:
return True
if o_type is list:
# an empty list can be considered a subset
if not o_type:
return True
# on other hand, if superset list is empty, we return false
if not superset:
return False
first_o_type = type(o[0])
first_ss_type = type(superset[0])
if first_o_type != first_ss_type:
return False
if first_o_type is dict:
merged_ss, merged_o = {}, {}
for v in o:
if type(v) is dict: # type check to be safe
merged_o.update(v)
# On Python 3.9+, the syntax would be:
# merged_o |= v
for v in superset:
if type(v) is dict: # type check to be safe
merged_ss.update(v)
return is_subset(merged_ss, merged_o)
elif first_o_type is list:
for idx, o_value in enumerate(o):
try:
ss_value = superset[idx]
except IndexError:
return False
if not is_subset(ss_value, o_value):
return False
else:
return True
else: # a list of simple types, like [1, 2, 3]
for o_value in o:
if o_value not in superset:
return False
else:
return True
# it's a simple type - not list or dict
return superset == o
Run Code Online (Sandbox Code Playgroud)
请注意,它没有涵盖一些边缘情况 - 例如,listYAML 配置中的 s 具有混合数据类型,例如dict和str值的列表。我将让您决定如何处理这些边缘情况,以防也值得涵盖它们。
无论如何,以下是使用我们上面声明的辅助函数的方法:
import yaml
superset_config = yaml.safe_load("""
# toy example of all parameters a config file might have
- param_a: 1
- param_b:
- b1: 'a'
- b2: [1,2,3]
""")
subset_config = yaml.safe_load("""
# want all configs with these values
- param_a: 1
- param_b:
- b2: [1,2,3]
""")
assert is_subset(superset_config, subset_config)
subset_config[0]['param_c'] = 'test'
assert not is_subset(superset_config, subset_config)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
232 次 |
| 最近记录: |