Python中字典的深层副本


340

我想dict在python中制作一个深层副本。不幸的是,该.deepcopy()方法不存在dict。我怎么做?

>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = my_dict.deepcopy()
Traceback (most recent calll last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'deepcopy'
>>> my_copy = my_dict.copy()
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
7

最后一行应为3

我希望所做的修改my_dict不会影响快照my_copy

我怎么做?该解决方案应与Python 3.x兼容。


3
我不知道它是否重复,但这是:stackoverflow.com/questions/838642/python-dictionary-deepcopy非常接近。
charleslparker 2013年

Answers:


472

怎么样:

import copy
d = { ... }
d2 = copy.deepcopy(d)

Python 2或3:

Python 3.2 (r32:88445, Feb 20 2011, 21:30:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import copy
>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = copy.deepcopy(my_dict)
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
3
>>>

16
确实,这适用于我给的过于简单的示例。我的钥匙不是数字,而是对象。如果阅读复制模块文档,则必须声明密钥的__copy __()/ __ deepcopy __()方法。非常感谢您带领我到达那里!
OlivierGrégoire11年

3
Python 3.2和2.7代码有什么区别吗?他们似乎和我一样。如果是这样,最好是编写一个代码块并声明“适用于Python 3和2”
MestreLion 2014年

30
还值得一提的copy.deepcopy是线程不是安全的。很难学到这一点。另一方面,根据您的用例,它json.loads(json.dumps(d)) 线程安全的,并且运行良好。

1
@rob,您应该发布该评论作为答案。这是一个可行的选择。线程安全差异是一个重要的区别。
BuvinJ

3
@BuvinJ问题是json.loads无法解决python dict属性不是JSON可序列化的所有用例的问题。它可能会帮助那些只处理简单数据结构(例如通过API)的人,但是我认为这不足以完全回答OP的问题。
抢劫

35

dict.copy()是字典
id的浅表复制函数, 是内置函数,可为您提供变量的地址

首先,您需要了解“为什么会发生此特定问题?”

In [1]: my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}

In [2]: my_copy = my_dict.copy()

In [3]: id(my_dict)
Out[3]: 140190444167808

In [4]: id(my_copy)
Out[4]: 140190444170328

In [5]: id(my_copy['a'])
Out[5]: 140190444024104

In [6]: id(my_dict['a'])
Out[6]: 140190444024104

键“ a”的两个字典中都存在的列表地址指向同一位置。
因此,当您在my_dict中更改列表的值时,my_copy中的列表也会更改。


问题中提到的数据结构解决方案:

In [7]: my_copy = {key: value[:] for key, value in my_dict.items()}

In [8]: id(my_copy['a'])
Out[8]: 140190444024176

或者您可以使用上述的Deepcopy。


4
您的解决方案不适用于嵌套词典。因此,最好使用deepcopy。
查尔斯·普拉格

2
@CharlesPlager同意!但您还应该注意,列表切片对dict无效value[:]。解决方案是针对问题中提到的特定数据结构,而不是通用解决方案。
theBuzzyCoder

16

Python 3.x

从复制导入深度复制

my_dict = {'one': 1, 'two': 2}
new_dict_deepcopy = deepcopy(my_dict)

如果没有Deepcopy,我将无法从域字典中删除主机名字典。

没有Deepcopy,我会收到以下错误:

"RuntimeError: dictionary changed size during iteration"

...当我尝试从另一本字典中的字典中删除所需的元素时。

import socket
import xml.etree.ElementTree as ET
from copy import deepcopy

域是一个字典对象

def remove_hostname(domain, hostname):
    domain_copy = deepcopy(domain)
    for domains, hosts in domain_copy.items():
        for host, port in hosts.items():
           if host == hostname:
                del domain[domains][host]
    return domain

输出示例:[ orginal ] domains = {'localdomain':{'localhost':{'all':'4000'}}}

[new] domains = {''localdomain':{}}}

因此,这里发生的是我正在遍历字典的副本,而不是遍历字典本身。使用此方法,您可以根据需要删除元素。


-2

我喜欢Lasse V. Karlsen并从中学到了很多。我将其修改为以下示例,该示例很好地突出了浅字典副本和深副本之间的区别:

    import copy

    my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
    my_copy = copy.copy(my_dict)
    my_deepcopy = copy.deepcopy(my_dict)

现在,如果你改变

    my_dict['a'][2] = 7

并做

    print("my_copy a[2]: ",my_copy['a'][2],",whereas my_deepcopy a[2]: ", my_deepcopy['a'][2])

你得到

    >> my_copy a[2]:  7 ,whereas my_deepcopy a[2]:  3

您为什么认为此答案与Lasse V. Karlsen的答案不同?另一个答案没有说什么呢?
OlivierGrégoire

嗨,奥利维尔!我并不是想利用Lasse V. Karlsen的回答的优点-他从根本上解决了我所遇到的问题,我对他负有债务。我的评论没有什么不同,只是补充。出于简单的原因,它将“复制”与“深复制”进行了对比。这是我问题的根源,因为当我以等效方式使用它们时,我弄错了。干杯。
拉斐尔·蒙泰罗

-8

一个更简单(在我看来)的解决方案是创建一个新词典并用旧词典的内容进行更新:

my_dict={'a':1}

my_copy = {}

my_copy.update( my_dict )

my_dict['a']=2

my_dict['a']
Out[34]: 2

my_copy['a']
Out[35]: 1

这种方法的问题在于它可能不够“深入”。即没有递归的深度。对于简单的对象已经足够好了,但对于嵌套字典却不够。这是一个示例,可能不够深:

my_dict1={'b':2}

my_dict2={'c':3}

my_dict3={ 'b': my_dict1, 'c':my_dict2 }

my_copy = {}

my_copy.update( my_dict3 )

my_dict1['b']='z'

my_copy
Out[42]: {'b': {'b': 'z'}, 'c': {'c': 3}}

通过使用Deepcopy(),我可以消除半浅行为,但是我认为必须确定哪种方法适合您的应用程序。在大多数情况下,您可能并不在意,但应注意可能存在的陷阱...最后的示例:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

my_copy2
Out[46]: {'b': {'b': 'z'}, 'c': {'c': 3}}

12
这是该字典的浅表副本,不是发问者要的。它包含的对象本身不会被复制。浅复制的一种简单方法是my_dict.copy()
Blckknght
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.