将一个字典列表“扁平化”

29

所以我的目标是从:

fruitColourMapping = [{'apple': 'red'}, {'banana': 'yellow'}]
finalMap = {'apple': 'red', 'banana': 'yellow'}
一种方法我得到了:
 from itertools import chain
 fruits = list(chain.from_iterable([d.keys() for d in fruitColourMapping]))
 colour = list(chain.from_iterable([d.values() for d in fruitColourMapping]))
 return dict(zip(fruits, colour))

有没有更好的、更符合Python风格的方法?


你真的不想用这种方式。字典本质上是无序的,所以迭代两次字典(就像你构建fruitcolor列表一样)只是侥幸生成了两个正确顺序的值集合。 - Silas Ray
5
@sr2222不正确。文档明确说明,如果在迭代字典的键和值时没有同时对字典进行修改,那么它们的顺序是有保证的。 - Daniel Roseman
1
@DanielRoseman 不过,这并不是一个好习惯。如果线程安全成为问题,它就不会是线程安全的。而且,仅仅因为某个东西实现了与字典相同的接口,并不意味着它在这些细节方面的行为会完全像字典一样。这种方法在正常情况下可以正常工作,但一旦出现问题,你就会遇到难以追踪的微妙错误。避免这种情况很容易,所以最好这么做。 - Silas Ray
9个回答

43

为什么需要复制?

在Python 3中,您可以使用新的ChainMap

ChainMap将多个字典(或其他映射)组合在一起,创建一个可更新的视图。
底层映射存储在列表中。该列表是公开的,可以使用maps属性进行访问或更新。没有其他状态。查找会依次搜索底层映射,直到找到键。相比之下,写入、更新和删除只操作第一个映射。

您只需要以下内容即可(请更改名称以遵守Python命名约定):

from collections import ChainMap
fruit_colour_mapping = [{'apple': 'red'}, {'banana': 'yellow'}]
final_map = ChainMap(*fruit_colour_mapping)

然后您可以使用所有正常的映射操作:

# print key value pairs:
for element in final_map.items():
    print(element)

# change a value:
final_map['banana'] = 'green'    # supermarkets these days....

# access by key:
print(final_map['banana'])

1
+1 我认为这是 Python 3 中最酷的 dict 新增功能之一。我第一次看到它是在新版的 Python Cookbook 中。 - hughdbrown
ChainMap的搜索操作比Python字典慢得多。你应该将final_map转换为Python字典。 - haluk

35
{k: v for d in fruitColourMapping for k, v in d.items()}

32
finalMap = {}
for d in fruitColourMapping:
    finalMap.update(d)

只是好奇:如果其中一个“d”的值是{'apple': 'red','orange': 'orange'},它应该能够工作吗? - Alois Mahdal
这段代码将简单地将所有字典条目添加到finalMap中,因此它将添加appleorange键。 - nneonneo
当超越简单的示例时,请确保所有键都是全局唯一的,因为update(或以单个字典结尾的任何方法)将去重键。如果您需要所有重复项(例如,当所有字典具有相同的键时),请在连接之前将字典转换为pandas DataFrames。 - mirekphd

14

不要去分解和重建,只需复制并更新:

final_map = {}
for fruit_color_definition in fruit_color_mapping:
    final_map.update(fruit_color_definition)

6
dict(d.items()[0] for d in fruitColourMapping)

6

方法

使用reduce函数将每个字典应用到一个空初始化器上。由于dict.update总是返回None,因此使用d.update(src) or d来赋予reduce所需的返回值。

代码

final_dict = reduce(lambda d, src: d.update(src) or d, dicts, {})

测试

>>> dicts = [{'a': 1, 'b': 2}, {'b': 3, 'c': 4}, {'a': 6}]
>>> final_dict = reduce(lambda d, src: d.update(src) or d, dicts, {})
>>> final_dict
{'a': 6, 'c': 4, 'b': 3}

1
为了让代码看起来更加专业,你可以考虑使用新的字典合并语法和 OR 运算符的重载:final_dict = reduce(lambda x, y: x | y, dicts, {}) - Nei Neto
@NeiNeto,你的评论已经过时了。当我在2013年写这篇文章时,最新的Python版本是3.3.0。你建议我应该写的功能在接下来的几年中都不存在。 - hughdbrown
确实。这真是一个令人尴尬的时刻。幸运的是,我的评论不是针对你,Hugh,而是针对读者。顺便说一句,那个“word of the day”很不错,感觉就像来自vsauce一样。不过,你会考虑编辑你的回答吗? - Nei Neto

5

给定

d1, d2 = [{'apple': 'red'}, {'banana': 'yellow'}]

代码

在Python 3.5中,引入了字典解包(参见PEP 448):

{**d1, **d2}
# {'apple': 'red', 'banana': 'yellow'}

在Python 3.9中,引入了合并操作符(merge operator):文档
d1 | d2
# {'apple': 'red', 'banana': 'yellow'}

4

我想出了一个有趣的一句话。

>>> a = [{"wow": 1}, {"ok": 2}, {"yeah": 3}, {"ok": [1,2,3], "yeah": True}]
>>> a = dict(sum(map(list, map(dict.items, a)), []))
>>> a
{'wow': 1, 'ok': [1, 2, 3], 'yeah': True}

2
你可以尝试以下方法: finalMap = dict(item for mapping in fruitColourMapping for item in mapping.items()) 这行代码可以将一个包含多个字典的列表转换为一个字典。

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