【问题标题】:Python-how to merge and not update two dictionaries with strings as keys and values as list of strings?Python-如何合并而不更新两个字典,其中字符串作为键,值作为字符串列表?
【发布时间】:2017-09-02 21:37:31
【问题描述】:

给定两个字典:

d1={'cardinal':['VA','OH'], 'meadowlark':['OR']}
d2={'meadowlark':['WY'], 'cardinal':['VA', 'IL'], 'oriole':['MD']}

如何将它们合并到一个新字典中更新原始字典像这样连接重复:

new_dict={'cardinal':['VA','OH','VA','IL'],'meadowlark':['OR', 'WY'], 'oriole':['MD']}

我的代码没有正确连接,但它是:

def merge(d1,d2):
    import collections
    a=collections.Counter(d1)
    b=collections.Counter(d2)
    return a+b

我不知道如何连接项目。

【问题讨论】:

  • 是的,但我也无法覆盖,我需要保留重复项
  • 在 d2 中 IL 发生了什么?
  • 我打错了。我刚刚编辑到正确的版本。
  • 顺便说一句,如果您不想在值中重复项,则可以使用集合作为值而不是列表,因此在new_dict 中,与'cardinal' 关联的值将是{'VA', 'OH', 'IL'}

标签: python dictionary merge concatenation


【解决方案1】:

您可以使用字典理解创建累积值字典:

def merge(d1, d2):
    return {k: d1.get(k, []) + d2.get(k, []) for k in d1.viewkeys()|d2.viewkeys()}

print(merge(d1, d2))
# {'meadowlark': ['OR', 'WY'], 'cardinal': ['VA', 'OH', 'VA', 'IL'], 'oriole': ['MD']}

在 Python 3 中将 .viewkeys 替换为 .keys


时间安排:

In [11]: %%timeit
    ...: merge2(d1, d2) # deepcopy-extend, timing for import excluded
    ...: 
    ...: 
100000 loops, best of 3: 11.1 µs per loop

In [13]: %%timeit
    ...: merge(d1, d2) # dict. comp.
    ...: 
100000 loops, best of 3: 2.32 µs per loop

【讨论】:

  • 不错。比较不同方法的效率会很有趣。我很确定.extend 比用+ 连接列表更有效; OTOH,迭代关键视图的联合可能比单独循环每个字典更有效。
  • @PM2Ring 你是对的,但extend 之前有一个deepcopy,这会影响性能。向量化的for 和零赋值是理解的另一个优点。
  • 这比我预期的要大!但我猜deepcopy 并不便宜,因为它必须处理包含任意对象的任意嵌套结构,因此它必须检查它复制的每个项目。
  • 我不确定您所说的“矢量化 for”是什么意思:理解(或生成器表达式)中的 for 循环是标准 Python for 循环,而不是快速的 C 风格在处理本机机器数据类型的数组时,可以使用像 Numpy 这样的循环。列表组合比等效的传统 for 循环和 .append 代码更快,因为它使用快速 LIST_APPEND 字节码而不是调用 .append 方法,因此您可以获得使用运算符而不是函数调用的优势;我不确定 dict comps 中会发生什么...
  • @PM2Ring Vectorized 可能不是正确的词,但 for 循环并不完全相同github.com/python/cpython/blob/…
【解决方案2】:

深复制其中一个字典,根据需要使用另一个字典中的值的元素扩展值。

>>> from copy import deepcopy
>>> d1={'cardinal':['VA','OH'], 'meadowlark':['OR']}
>>> d2={'meadowlark':['WY'], 'cardinal':['VA', 'IL'], 'oriole':['MD']}
>>> 
>>> d = deepcopy(d1)
>>> for key, lst in d2.items():
...     d.setdefault(key, []).extend(lst)
... 
>>> d
{'meadowlark': ['OR', 'WY'], 'oriole': ['MD'], 'cardinal': ['VA', 'OH', 'VA', 'IL']}
>>> d1
{'meadowlark': ['OR'], 'cardinal': ['VA', 'OH']}
>>> d2
{'meadowlark': ['WY'], 'oriole': ['MD'], 'cardinal': ['VA', 'IL']}

【讨论】:

  • 完美时机哈哈
【解决方案3】:

此代码类似于 timgeb 的,只是我们手动进行深度复制。深拷贝对于防止改变原始列表是必要的。 dict 的浅拷贝(例如,使用dict.copy 方法)会生成一个新的dict 对象,但它会重新使用原始的dict 键和值。正如您所注意到的,当值是可变的(dict 键不应该是可变的)时,这可能会导致问题。

def append_dict(new_dict, old_dict):
    for k, v in old_dict.items():
        new_dict.setdefault(k, []).extend(v)

d1 = {'cardinal':['VA','OH'], 'meadowlark':['OR']}
d2 = {'meadowlark':['WY'], 'cardinal':['VA', 'IL'], 'oriole':['MD']}

new_dict = {}
append_dict(new_dict, d1)
append_dict(new_dict, d2)

print('new', new_dict)
print('d1', d1)
print('d2', d2)

输出

new {'cardinal': ['VA', 'OH', 'VA', 'IL'], 'meadowlark': ['OR', 'WY'], 'oriole': ['MD']}
d1 {'cardinal': ['VA', 'OH'], 'meadowlark': ['OR']}
d2 {'meadowlark': ['WY'], 'cardinal': ['VA', 'IL'], 'oriole': ['MD']}

【讨论】:

    【解决方案4】:

    你可以做的很简单,一步一步来:

     d1={'cardinal':['VA','OH'], 'meadowlark':['OR']}
    
    d2={'meadowlark':['WY'], 'cardinal':['VA', 'IL'], 'oriole':['MD']}
    
    new_d = d1.copy()
    
    for k, v in d2.items():
        if k not in new_d:
            new_d[k] = v
        else:
            new_d[k] = new_d[k] + v
    

    它看起来并不酷,但它确实有效:)

    【讨论】:

    • 请解释一下:)
    • 糟糕,我错过了复制部分,但是您需要 deepcopy 为新字典创建新列表。
    • 正如我在回答中所说,仅执行d1.copy() 将重用new_d 中的d1 列表,因此当您执行new_d[k] += v 时,将修改d1。您可以使用deepcopy,或者干脆使用new_d[k] = new_d[k] + v,这将为new_d[k]分配一个新列表
    • 这是一个很好的解释,我根据您的评论编辑了我的答案。
    猜你喜欢
    • 2021-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-07
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 2021-01-29
    相关资源
    最近更新 更多