我有一本字典,我想将其用作模板,以使用更新的字典项生成多个字典。在pytest的单元测试中,此列表应用作测试目的的数据集。
我在代码中使用以下构造(不包括检查项):
def _f(template,**kwargs):
result = [template]
for key, value in kwargs.items():
result = [dict(template_item,**dict([(key,v)])) for v in value for template_item in result]
return result
template = {'a': '', 'b': '', 'x': 'asdf'}
r = _f(template, a=[1,2],b=[11,22])
pprint(r)
[{'a': 1, 'b': 11, 'x': 'asdf'},
{'a': 2, 'b': 11, 'x': 'asdf'},
{'a': 1, 'b': 22, 'x': 'asdf'},
{'a': 2, 'b': 22, 'x': 'asdf'}]
我想问一下该结构是否足以构建良好的结构-可能可以更高效地编写它。
这是准备测试数据的正确方法吗?
编辑: 特别是我不确定
[dict(template_item,**dict([(key,v)])) for v in value for template_item in result]
和
dict(template_item,**dict([(key,v)]))
在我考虑dict.update()之前,但由于它不返回字典,因此不适合理解。
然后我在考虑像这样的简单语法
d = {'aa': 11, 'bb': 22}
dict(d,x=33,y=44)
{'aa': 11, 'bb': 22, 'x': 33, 'y': 44}
但是我无法通过变量传递键值。而且创建dict只是为了解开包装对我来说适得其反。
答案 0 :(得分:2)
特别是我不确定...
在理解中更新Python字典的事情有点复杂,因为它们是可变的。在Why doesn't a python dict.update() return the object?中,最佳答案建议您当前的解决方案。我个人可能会在此处使用常规的for循环,以确保代码清晰易读。
这是准备测试数据的正确方法吗?
您可以使用itertools.product
来简化代码。
可以删除template
参数(因为您可以在**kwargs
中传递模板变量名称及其可能的值):
from pprint import pprint
import itertools
def _f(**kwargs):
keys, values = zip(*(kwargs.items())) # 1.
subsets = [subset for subset in itertools.product(*values)] # 2.
return [
{key: value for key, value in zip(keys, subset)} for subset in subsets
] # 3.
r = _f(a=[1, 2], b=[11, 22], x=['asdf'])
pprint(r)
现在每个步骤都发生了什么:
步骤1。 您将关键字dict分为键和值。这很重要,因此您将确定每次迭代这些参数的顺序。此时的键和值如下所示:
keys = ('a', 'b', 'x')
values = ([1, 2], [11, 22], ['asdf'])
步骤2。您计算值的笛卡尔积,这意味着您从每个values
列表中获取一个值的所有可能组合。该操作的结果如下:
subsets = [(1, 11, 'asdf'), (1, 22, 'asdf'), (2, 11, 'asdf'), (2, 22, 'asdf')]
第3步。 现在,您需要将每个键映射到每个子集中的相应值,因此列表和字典的理解应该与您使用先前方法计算的结果完全相同:
[{'a': 1, 'b': 11, 'x': 'asdf'},
{'a': 1, 'b': 22, 'x': 'asdf'},
{'a': 2, 'b': 11, 'x': 'asdf'},
{'a': 2, 'b': 22, 'x': 'asdf'}]