Python中字典的深层副本

问题:Python中字典的深层副本

我想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兼容。

I would like to make a deep copy of a dict in python. Unfortunately the .deepcopy() method doesn’t exist for the dict. How do I do that?

>>> 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

The last line should be 3.

I would like that modifications in my_dict don’t impact the snapshot my_copy.

How do I do that? The solution should be compatible with Python 3.x.


回答 0

怎么样:

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
>>>

How about:

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

Python 2 or 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
>>>

回答 1

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。

dict.copy() is a shallow copy function for dictionary
id is built-in function that gives you the address of variable

First you need to understand “why is this particular problem is happening?”

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

The address of the list present in both the dicts for key ‘a’ is pointing to same location.
Therefore when you change value of the list in my_dict, the list in my_copy changes as well.


Solution for data structure mentioned in the question:

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

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

Or you can use deepcopy as mentioned above.


回答 2

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’:{}}}

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

Python 3.x

from copy import deepcopy

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

Without deepcopy, I am unable to remove the hostname dictionary from within my domain dictionary.

Without deepcopy I get the following error:

"RuntimeError: dictionary changed size during iteration"

…when I try to remove the desired element from my dictionary inside of another dictionary.

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

domain is a dictionary object

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

Example output: [orginal]domains = {‘localdomain’: {‘localhost’: {‘all’: ‘4000’}}}

[new]domains = {‘localdomain’: {} }}

So what’s going on here is I am iterating over a copy of a dictionary rather than iterating over the dictionary itself. With this method, you are able to remove elements as needed.


回答 3

我喜欢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

I like and learned a lot from Lasse V. Karlsen. I modified it into the following example, which highlights pretty well the difference between shallow dictionary copies and deep copies:

    import copy

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

Now if you change

    my_dict['a'][2] = 7

and do

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

you get

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

回答 4

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

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}}

A simpler (in my view) solution is to create a new dictionary and update it with the contents of the old one:

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

The problem with this approach is it may not be ‘deep enough’. i.e. is not recursively deep. good enough for simple objects but not for nested dictionaries. Here is an example where it may not be deep enough:

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}}

By using Deepcopy() I can eliminate the semi-shallow behavior, but I think one must decide which approach is right for your application. In most cases you may not care, but should be aware of the possible pitfalls… final example:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

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