如何复制一个字典并在一行代码中修改它

Bor*_*lik 34 python coding-style

我经常需要创建一个或两个项目彼此不同的词汇.这是我通常做的事情:

setup1 = {'param1': val1, 
            'param2': val2,
            'param3': val3,
            'param4': val4,
            'paramN': valN}

setup2 = copy.deepcopy(dict(setup1))
setup2.update({'param1': val10, 
                   'param2': val20})
Run Code Online (Sandbox Code Playgroud)

程序中有一个点setup2是相同副本的事实setup1让我感到紧张,因为我担心在程序生命的某个时刻,两条线可能会分开,这对于太多的bug来说是一个滑坡.

理想情况下,我希望能够在一行代码中完成此操作(类似这样):

setup2 = dict(setup1).merge({'param1': val10, 
                        'param2': val20})
Run Code Online (Sandbox Code Playgroud)

当然,我可以使用分号将两个命令压缩到一个物理行中,但这对我来说看起来很难看.还有其他选择吗?

Den*_*Kim 50

在我看来,最简单的方法是这样的:

new_dict = {**old_dict, 'changed_val': value, **other_new_vals_as_dict}
Run Code Online (Sandbox Code Playgroud)

  • 小修改:`new_dict = {**copy.deepcopy(old_dict), 'changed_val': value, **other_new_vals_as_dict}` (6认同)
  • 注意:在`old_dict` 包含嵌套词典的情况下,我认为它们不会被复制,而是在`new_dict` 中被引用。“new_dict”中嵌套条目的后续变化因此也会修改“old_dict”的嵌套条目。 (5认同)
  • 请注意,这仅适用于Python 3.5+,请参阅https://www.python.org/dev/peps/pep-0448/ (2认同)

zin*_*del 20

setup2 = dict(setup1.items() + {'param1': val10, 'param2': val20}.items())
Run Code Online (Sandbox Code Playgroud)

这样,如果添加了新密钥,则会setup1替换旧密钥/值对.

  • 这是否绑定到特定的Python版本?在3.4中,这会返回“ TypeError:+不支持的操作数类型”:“ dict_items”和“ dict_items”。 (2认同)
  • 是的,这是 Python 2 (2认同)

Luc*_*ert 20

您可以在字典构造函数中使用关键字参数进行更新

new = dict(old, a=1, b=2, c=3)

# You can also unpack your modifications
new = dict(old, **mods)
Run Code Online (Sandbox Code Playgroud)

这相当于:

new = old.copy()
new.update({"a": 1, "b": 2, "c": 3})
Run Code Online (Sandbox Code Playgroud)

资源

注意:dict.copy()创建浅表副本.

  • 从所有答案中,这就是我要找的! (3认同)
  • 这意味着您可以非常方便地执行 `new = dict(old, **mods)`。 (2认同)
  • 为什么这不是最佳答案?! (2认同)

Ada*_*tan 15

为此构建一个功能.

当您在代码中使用它时,您的意图会更清晰,您可以在一个地方处理复杂的决策(例如,深拷贝和浅拷贝).

def copy_dict(source_dict, diffs):
    """Returns a copy of source_dict, updated with the new key-value
       pairs in diffs."""
    result=dict(source_dict) # Shallow copy, see addendum below
    result.update(diffs)
    return result
Run Code Online (Sandbox Code Playgroud)

现在副本是原子的,假设没有线程涉及:

setup2=copy_dict(setup1, {'param1': val10, 'param2': val20})
Run Code Online (Sandbox Code Playgroud)

附录 - 深层复制

对于基元(整数和字符串),不需要深层复制:

>>> d1={1:'s', 2:'g', 3:'c'}
>>> d2=dict(d1)
>>> d1[1]='a'
>>> d1
{1: 'a', 2: 'g', 3: 'c'}
>>> d2
{1: 's', 2: 'g', 3: 'c'}
Run Code Online (Sandbox Code Playgroud)

如果您需要深层副本,请使用该copy模块:

result=copy.deepcopy(source_dict) # Deep copy
Run Code Online (Sandbox Code Playgroud)

代替:

result=dict(setup1)               # Shallow copy
Run Code Online (Sandbox Code Playgroud)

确保字典中的所有对象都支持深层复制(任何可以pickled做的对象).

  • `现在副本是原子的,假设没有线程参与.你是什么意思? (4认同)

Sve*_*ach 13

setup2 = dict((k, {'param1': val10, 'param2': val20}.get(k, v))
              for k, v in setup1.iteritems())
Run Code Online (Sandbox Code Playgroud)

这仅在更新字典的所有键都已包含在内时才有效setup1.

如果你的所有键都是字符串,你也可以这样做

setup2 = dict(setup1, param1=val10, param2=val20)
Run Code Online (Sandbox Code Playgroud)

  • @Adam:这不是字典理解,因此在Python 2.6中工作正常. (2认同)

Igo*_*bia 7

从Python 3.9开始,您可以使用管道命令(例如first_dic | second_dic)来合并字典;它还可用于通过首先传递原始字典并将更新作为第二个字典来返回新的更新字典:

setup2 = setup1 | {'param1': val10, 'param2': val20}
Run Code Online (Sandbox Code Playgroud)