创建一个包含值列表的单独字典。

3
我有一个这样的字典:
d = {
    'hosts': [
        {'hostname': 'abc', 'ip': '127.0.0.1', 'extra_check_cmd': 'check-me'},
        {'hostname': 'def', 'ip': '127.0.0.2', 'extra_check_cmd': 'check-it,check-this'},
        {'hostname': 'ijk,uvw,xyz', 'ip': '127.0.0.3,127.0.0.4,127.0.0.5', 'extra': 'check-me,check-this,check-it'}
    ]
}

我想根据它创建以下字典
d = {
    'hosts': [
        {'hostname': 'abc', 'ip': '127.0.0.1', 'extra_check_cmd': 'check-me'},
        {'hostname': 'def', 'ip': '127.0.0.2', 'extra_check_cmd': 'check-it'},
        {'hostname': 'def', 'ip': '127.0.0.2', 'extra_check_cmd': 'check-this'},
        {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-me'},
        {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-me'},
        {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-me'}
        {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-it'},
        {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-it'},
        {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-it'}
        {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-this'},
        {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-this'},
        {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-this'}
    ]
}

这意味着每个值列表都应该在给定值列表时有单独的子字典。
1个回答

2
也许是这样的吗?
def expand_dict(host):
    # Create all of the possible key-value pairs for each key in the original dictionary
    kv_pairs = [[(k, v) for v in vals.split(",")] for k, vals in host.items()]
    # Find the number of dictionaries this would expand to
    max_len = max(len(p) for p in kv_pairs)
    # A list of possible values must either be the length of the number of dictionaries we expect, or length 1 so we can repeat the value max_len times
    assert all(len(pairs) in {1, max_len} for pairs in kv_pairs)
    # Expand all of the length 1 value lists to length max_len
    updated_pairs = [p if len(p) == max_len else p * max_len for p in kv_pairs]
    # Return a generator of dictionaries for each of the sets of key-value pairs
    return (dict(pairs) for pairs in zip(*updated_pairs))

input_dict = {'hosts': [{'hostname': 'abc', 'ip': '127.0.0.1', 'extra_check_cmd': 'check-me'}, {'hostname': 'def', 'ip': '127.0.0.2', 'extra_check_cmd': 'check-it,check-this'}, {'hostname': 'ijk,uvw,xyz', 'ip': '127.0.0.3,127.0.0.4,127.0.0.5', 'extra': 'check-me'}]}
output_dict = {'hosts': [d for host in input_dict['hosts'] for d in expand_dict(host)]}

进一步分解,让我们用一个例子来尝试。在这种情况下,我使用 host = d['hosts'][2]
{'hostname': 'ijk,uvw,xyz',
 'ip': '127.0.0.3,127.0.0.4,127.0.0.5',
 'extra': 'check-me'}

这行代码 kv_pairs = [[(k, v) for v in vals.split(",")] for k, vals in host.items()] 给我们提供了一个内部列表项可能的键值对列表。

[
    [('hostname', 'ijk'), ('hostname', 'uvw'), ('hostname', 'xyz')],
    [('ip', '127.0.0.3'), ('ip', '127.0.0.4'), ('ip', '127.0.0.5')],
    [('extra', 'check-me')],
]

如您所见,原始主机字典中每个“hostname”和“ip”键都有3个键值对,而“extra”键只有1个键值对。目标是生成3个带有“extra”:“check-me”的字典。因此,我们需要找到预期生成的字典数量。
该行代码“max_len = max(len(p) for p in kv_pairs)”给出了3。接着,为了保证问题定义得当,我们要确保在“kv_pairs”中每组键值对要么长度为1,要么长度为3,如果其它长度则该问题就没有良好定义,因此添加断言“assert all(len(pairs) in {1, max_len} for pairs in kv_pairs)”。
然后,我们通过重复来扩展所有长度为1的键值对列表,使它们的长度为3。这个列表推导式基本上接受所有长度为3的列表,并将长度为1的列表重复3次,使它们的长度都相同。
updated_pairs = [p if len(p) == max_len else p * max_len for p in kv_pairs]

[[('hostname', 'ijk'), ('hostname', 'uvw'), ('hostname', 'xyz')],
 [('ip', '127.0.0.3'), ('ip', '127.0.0.4'), ('ip', '127.0.0.5')],
 [('extra', 'check-me'), ('extra', 'check-me'), ('extra', 'check-me')]]

现在一切都整齐地排列好了,我们可以开始创建字典。我们可以使用 zip() 来实现这一点,它会给我们返回迭代器,其中包含每个输入迭代器中的项目组成的元组。我使用 Python 的拆包语法将 updated_kv_pairs 中的每个列表都扩展为单独的参数传递给 zip()。换句话说,

zip(*updated_kv_pairs)

等同于

zip(updated_kv_pairs[0], updated_kv_pairs[1], updated_kv_pairs[2])

zip() 的每次迭代会给我们提供一组键值对列表,用于输出一个字典。这使得我们能够:

{'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-me'}
{'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-me'}
{'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-me'}

是的,我在我的代码中没有使用字典,这只是为了在这里进行解释。非常感谢你及时的回复。 - Hardy Sandhu
@HardySandhu 嗯,你试过了吗?;-) - Stef
@rchome 这个答案中的代码很棒,但是配合文字解释会更好! - Stef
我也无法通过https://pythontutor.com/理解它。稍微解释一下会更好。 - Hardy Sandhu
我已经更新了代码并添加了一些注释。如果有任何需要更清晰的地方,请告诉我。 - rchome
你能否不使用函数式编程,或者换句话说,只用直接的Python代码来编写上述代码?如果可能的话?@rchome - Hardy Sandhu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接