更新Python字典时如何添加到现有键?

11

我正在寻找一种最高效且符合Python风格(主要是高效)的方法来更新字典,但如果存在相同键,则保留旧值。例如...

myDict1 = {'1': ('3', '2'), '3': ('2', '1'), '2': ('3', '1')}
myDict2 = {'4': ('5', '2'), '5': ('2', '4'), '2': ('5', '4')}

myDict1.update(myDict2) gives me the following....

{'1': ('3', '2'), '3': ('2', '1'), '2': ('5', '4'), '5': ('2', '4'), '4': ('5', '2')}
请注意,键“2”存在于两个字典中并且曾经具有值(“3”,“1”),但现在它具有来自myDict2中其键的值(“5”,“4”). 有没有一种有效的方式可以更新字典,以使键“2”最终具有值(“3”,“1”,“5”,“4”)?#顺序无关紧要 谢谢您提前。

2
不,没有简单的方法可以做到。您必须迭代键并合并值。 - iurisilvio
那很不幸,感谢您的回复。 - Ogen
看起来你正在用邻接表表示一个图...那么如果'2':('3','1')'2':('5','3')合并,结果列表中会有两个'3'吗? - Apiwat Chantawibul
@Billiska 然后 {'3': ['1', '5'], '1': ['3'], '5':[3]} - Ogen
5个回答

11

我认为最有效的方法是这样做:

for k, v in myDict2.iteritems():
    myDict1[k] = myDict1.get(k, ()) + v

不幸的是,对于你想要做的事情,没有相应的update等效方法。


我现在会尝试这种方法,看看它是否能够处理非常大的字典,并回复你。感谢你的回复。 - Ogen
@Clay,我更新了我的答案,使得你的迭代次数更少,也许值得尝试我的新解决方案。然而,在某个时候,处理大量数据只是需要花费很多时间。 - Nolen Royalty

5
两个原地更新操作有什么问题?
myDict2.update(myDict1)
myDict1.update(myDict2)

解释: 第一次更新将用myDict1中的值覆盖已经存在的键,并插入不存在的所有键值对。
第二次更新将用myDict2中的值覆盖myDict1中已经存在的键,这些值实际上是来自于第一个操作中的myDict1。任何新插入的键值对都来自于原始的myDict2。
当然,前提条件是您不关心保留myDict2。
更新:在Python3中,您可以在不触及myDict2的情况下完成此操作。
myDict1 = {**myDict1, **myDict2, **myDict1}

这实际上与之前的相同

myDict1 = {**myDict2, **myDict1}

输出

{'1': ('3', '2'), '3': ('2', '1'), '2': ('3', '1'), '4': ('5', '2'), '5': ('2', '4')}

2在某些情况下不起作用:例如:<code> myDict1: { "pg": { "username": "foo", "password": "bar"} } myDict2: {"pg": {"host": "localhost"} </code> <code> myDict2.update(myDict1) myDict2: { "pg": { "username": "foo", "password": "bar"} } </code> <code> myDict1.update(myDict2) myDict1: { "pg": { "username": "foo", "password": "bar"} } </code> - gdenn

4

合并大型字典的最快方法是引入一个中间对象,该对象表现为已合并字典,但实际上并未合并它们(参见@Raymond Hettinger's answer):

from collections import ChainMap

class MergedMap(ChainMap):
    def __getitem__(self, key):
        result = []
        found = False
        for mapping in self.maps:
            try:
                result.extend(mapping[key])
                found = True
            except KeyError:
                pass
        return result if found else self.__missing__(key)

merged = MergedMap(myDict1, myDict2)

无论是否适用取决于您以后如何使用合并字典。它使用Python 3.3+的collections.ChainMap方便地提供完整的MutableMapping接口;您可以在旧版本的Python上仅实现您使用的部分。

3
也许使用一个defaultdict会有帮助。
from collections import defaultdict
myDict0= {'1': ('3', '2'), '3': ('2', '1'), '2': ('3', '1')}
myDict2 = {'4': ('5', '2'), '5': ('2', '4'), '2': ('5', '4')}
myDict1 = defaultdict(list)
for (key, value) in myDict0.iteritems():
     myDict1[key].extend(value)

for (key, value) in myDict2.iteritems():
     myDict1[key].extend(value)

print myDict1
defaultdict(<type 'list'>, {'1': ['3', '2'], '3': ['2', '1'], '2': ['3', '1', '5', '4'], '5': ['2', '4'], '4': ['5', '2']})

0

很抱歉,没有简单的方法可以做到。

最好的方法可能是迭代和合并。类似这样:

for key in myDict1.iterkeys():
    # Thank you to user2246674 and Nolen Royalty to help me optimise this in their comments 
    if key in myDict2:
        myDict2[key] = myDict2[key] + myDict1[key]
    else:
        myDict2[key] = myDict1[key]

谢谢回复,你的答案非常有效,但是我有一个包含100000+键的字典,它需要太长时间,所以我正在寻找一种高效的原地方法。 - Ogen
抱歉 - 我刚刚尝试了 200,000 个键,你是对的。需要一段时间!很抱歉 :-( - Ewan
使用 if key in myDict2if key in myDict2.iterkeys() 更高效。key in myDict2 的时间复杂度为 O(1),而 key in myDict2.iterkeys() 的时间复杂度为 O(n),其中 n 是 myDict2 的长度。我相信这就是 @user2246674 要表达的意思。 - Nolen Royalty
这与myDict2.update(myDict1)具有完全相同的效果,这不是我要寻找的。 - Ogen

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