为包含列表的字典创建列表推导式
我需要创建一个列表推导式,从一个嵌套的列表中提取字典的值。到目前为止,我的尝试都没有成功。这个对象看起来是这样的:
MyList=[[{'animal':'A','color':'blue'},{'animal':'B','color':'red'}],[{'animal':'C','color':'blue'},{'animal':'D','color':'Y'}]]
我想提取字典/列表/列表中每个元素的值,这样我就能得到两个新的列表:
Animals=[[A,B],[C,D]]
Colors=[[blue,red],[blue,Y]]
有什么建议吗?不一定要用列表推导式;这只是我目前的起点。谢谢!
2 个回答
在一次赋值中(通过一个列表推导式,以及使用 map
和 zip
的帮助):
Colors, Animals = map(list,
zip(*[map(list,
zip(*[(d['color'], d['animal']) for d in a]))
for a in MyList]))
如果你对元组没有意见,可以省去两次调用 map => list 的步骤。
编辑:
让我们详细看看,通过分解嵌套的推导式来理解。我们还假设 MyList
有 m
个元素,总共有 n
个对象(字典)。
[[d for d in sub] for sub in MyList]
这段代码会遍历每个子列表中的字典。对于每个字典,我们创建一个包含它的 color
属性作为第一个元素,animal
属性作为第二个元素的元组:
(d['color'], d['animal'])
到目前为止,这个过程的时间复杂度是 O(n)
- 实际上会处理 n
个元素。
print [[(d['color'], d['animal']) for d in sub] for sub in MyList]
现在,对于原始列表中的每个 m
个子列表,我们有一组元组需要进行 解压,也就是把它变成两个单独的列表。在 Python 中,解压 是通过 zip
函数来完成的,传入一组元组作为参数(第一个元组的元素个数决定了输出的元组数量)。例如,传入 3 个元组,我们会得到两个各有 3 个元素的列表。
>>> zip((1,2), (3,4), (5,6)) #Prints [(1, 3, 5), (2, 4, 6)]
为了将这个应用到我们的情况,我们需要将一组元组传递给 zip
,作为可变数量的参数:这可以通过 splat 操作符来实现,也就是 *
[zip(*[(d['color'], d['animal']) for d in sub]) for sub in MyList]
这个操作需要遍历每个子列表一次,然后再遍历我们在前一步创建的每个元组。因此,总的运行时间是 O(n + n + m)
= O(n)
,大约需要 2*n + 2*m
次操作。
到目前为止,我们有 m
个子列表,每个子列表包含两个元组(第一个元组会收集该子列表的所有颜色,第二个元组会收集所有动物)。为了得到两个各有 m
个元组的列表,我们再次进行解压。
zip(*[zip(*[(d['color'], d['animal']) for d in sub]) for sub in MyList]
这将需要额外的 m
步骤 - 所以运行时间仍然是 O(n)
,大约需要 2*n + 4*m
次操作。
为了简单起见,我们在这个分析中省略了将元组映射到列表的步骤 - 如果你对元组没意见,这样做是可以的。
不过,元组是不可变的,所以这可能不适用。如果你需要列表的列表,你需要对每个元组应用 list
函数:每个 m
个子列表一次(总共有 2*n
个元素),以及对每个第一层列表,即动物和颜色各一次(每个都有 m
个元素)。假设 list
的时间复杂度与它所应用的序列长度成正比,这个额外的步骤需要 2*n + 2*m
次操作,这仍然是 O(n)
。
Animals = [[d['animal'] for d in sub] for sub in MyList]
Colors = [[d['color'] for d in sub] for sub in MyList]
得到了想要的结果:
[['A', 'B'], ['C', 'D']]
[['blue', 'red'], ['blue', 'Y']] # No second 'red'.
我在这里做的就是先拿到每个子列表,然后再拿到每个字典,最后访问正确的键。