python - 通过理解更新列表中的字典项
问题描述
我有一本字典,我想将其用作模板来生成具有更新字典项的多个字典。此列表应用作 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 只是为了打开它对我来说听起来适得其反。
解决方案
特别是我不确定...
在推导式中更新 Python dicts 的事情有点复杂,因为它们是可变的。在为什么 python dict.update() 不返回对象?最佳答案建议您当前的解决方案。就个人而言,我可能会在这里使用常规的 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'}]
推荐阅读
- java - 试图找到一种从中心窗格控制器调用边框窗格上的 setCenter 方法的方法
- javascript - 如何正确地从 Javascript 中的函数返回结果?
- email - 在电子邮件信封中添加接收字段
- java - 如何在 if 语句中使用 setOpaque(true/false)
- google-sheets - 如何在更短的时间内使用谷歌表格创建电报民意调查
- github - 当我离开组织时,github 贡献图是否会删除专门对该组织所做的贡献?
- vue.js - 将 @contextmenu 事件与 vuetify v-data-table 一起使用
- php - 计算平均值 - 声明变量
- django - 如何在 Django 管理页面中嵌套子模型或在它们之间添加链接?
- flutter - Flutter:如何在 photo_manager 包中使用 filterOption