将Python 3中的字典映射更新方法应用于其他字典列表

12

在Python 2中,我可以进行以下操作:

>> d = {'a':1}
>> extras = [{'b':2}, {'c':4}]
>> map(d.update, extras)
>> d['c']
>> 4

在Python 3中,如果获取一个不存在的键,则会引发一个 KeyError 错误:

>> d = {'a':1}
>> extras = [{'b':2}, {'c':4}]
>> map(d.update, extras)
>> d['c']
>> KeyError: 'c'

我希望在Python 3中实现与Python 2相同的行为。

我理解在Python 3中,map函数将返回一个迭代器(延迟评估等),必须对其进行迭代才能更新字典。

我曾假设d['c']键查找会以某种方式触发map迭代,但事实并非如此。

有没有一种Python风格的方法可以在不编写for循环的情况下实现这种行为,我觉得与map相比,for循环过于冗长。

我考虑过使用列表推导:

>> d = {'a':1}
>> extras = [{'b':2}, {'c':4}]
>> [x for x in map(d.update, extras)]
>> d['c']
>> 4

但它似乎不符合Python的风格。


1
list(map(d.update, extras)) 会强制迭代器被消耗。 - jonrsharpe
3
Pythonic的方式是使用for循环!使用map(或列表推导式)只为了其副作用而不是其结果,绝对是Pythonic的做法。 - Mark Dickinson
2个回答

12

正如您所指出的,Python 3中的map创建了一个迭代器,这本身并不会导致任何update发生:

>>> d = {'a': 1}
>>> extras = [{'b':2}, {'c':4}]
>>> map(d.update, extras)
<map object at 0x105d73c18>
>>> d
{'a': 1}

为了完全评估 map,您可以显式地将其传递给 list:
>>> list(map(d.update, extras))
[None, None]
>>> d
{'a': 1, 'b': 2, 'c': 4}

然而,正如Python 3的新特性相关章节所描述的那样:

特别棘手的是针对函数副作用调用map();正确的转换方式是使用普通的for循环(因为创建列表只会浪费资源)。

在您的情况下,代码应该像这样:
for extra in extras:
    d.update(extra)

这样就不会出现不必要的 None 列表。


那个for循环会让我崩溃的!无论如何还是谢谢。 - mattgathu
2
注:可以使用 collections.deque(iterable, maxlen=0) 代替 list 来消耗 iterable 而不消耗内存。这也只执行一个字节码,因此至少在 CPython 中应该比显式 for 更有效率一些。 - Bakuriu
@Bakuriu 很有趣,我没想到可以这样做;我不知道 deque 可以 有零最大长度! - jonrsharpe
精彩的解释 - Ihor Mak

1
在@jonrsharpe清晰解释了问题之后,你可以在Python 3中使用collections.ChainMap来完成这样的任务:
>>> from collections import ChainMap
>>> chain=ChainMap(d, *extras)
>>> chain
ChainMap({'a': 1}, {'b': 2}, {'c': 4})
>>> chain['c']
4

但请注意,如果有重复的键,则使用第一个映射中的值。
了解更多关于使用ChainMap的优点:什么是collections.ChainMap的目的?

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