我设置了dict2 = dict1。当我编辑dict2时,原来的dict1也改变了。为什么?

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}

当前回答

虽然dict.copy()和dict(dict1)会生成一个副本,但它们只是浅副本。如果你想要一个深度拷贝,copy.deepcopy(dict1)是必需的。一个例子:

>>> source = {'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3}
>>> copy1 = source.copy()
>>> copy2 = dict(source)
>>> import copy
>>> copy3 = copy.deepcopy(source)
>>> source['a'] = 10  # a change to first-level properties won't affect copies
>>> source
{'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy3
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> source['b']['m'] = 40  # a change to deep properties WILL affect shallow copies 'b.m' property
>>> source
{'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy3  # Deep copy's 'b.m' property is unaffected
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}

关于浅拷贝和深拷贝,请参阅Python复制模块docs:

浅复制和深复制之间的区别只与复合对象(包含其他对象的对象,如列表或类实例)相关:

浅拷贝构造一个新的复合对象,然后(在可能的范围内)将对原始对象的引用插入其中。 深度复制构造一个新的复合对象,然后递归地将原始对象中的对象的副本插入其中。

其他回答

使用for循环进行复制:

orig = {"X2": 674.5, "X3": 245.0}

copy = {}
for key in orig:
    copy[key] = orig[key]

print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 674.5, 'X3': 245.0}
copy["X2"] = 808
print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 808, 'X3': 245.0}

深度和简单的记忆方法:

当你执行dict2 = dict1时,dict2指向dict1。dict1和dict2都指向内存中的相同位置。这只是在python中使用可变对象时的正常情况。当你在python中使用可变对象时,你必须小心,因为它很难调试。

你不应该使用dict2 = dict1,而是应该使用python的copy模块中的copy(浅拷贝)和deepcopy方法来分离dict2和dict1。

正确的做法是:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1.copy()
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?"
>>> dict2
{'key1': 'value1', 'key2': 'WHY?'}
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>> id(dict1)
140641178056312
>>> id(dict2)
140641176198960
>>> 

正如您可以看到的,dict1和dict2的id是不同的,这意味着它们都指向/引用内存中的不同位置。

此解决方案适用于具有不可变值的字典,但不适用于具有可变值的字典。

Eg:

>>> import copy
>>> dict1 = {"key1" : "value1", "key2": {"mutable": True}}
>>> dict2 = dict1.copy()
>>> dict2
{'key1': 'value1', 'key2': {'mutable': True}}
>>> dict2["key2"]["mutable"] = False
>>> dict2
{'key1': 'value1', 'key2': {'mutable': False}}
>>> dict1
{'key1': 'value1', 'key2': {'mutable': False}}
>>> id(dict1)
140641197660704
>>> id(dict2)
140641196407832
>>> id(dict1["key2"])
140641176198960
>>> id(dict2["key2"])
140641176198960

你可以看到,尽管我们对dict1应用了copy,但在dict2和dict1上,mutable的值都被更改为false,尽管我们只在dict2上更改了它。这是因为我们改变了dict1中可变dict部分的值。当我们在dict上应用复制时,它只会做一个浅复制,这意味着它将所有不可变的值复制到一个新的dict中,而不复制可变的值,但它会引用它们。

最终的解决方案是对dict1进行深度复制,以完全创建一个包含复制的所有值(包括可变值)的新dict。

>>>import copy
>>> dict1 = {"key1" : "value1", "key2": {"mutable": True}}
>>> dict2 = copy.deepcopy(dict1)
>>> dict2
{'key1': 'value1', 'key2': {'mutable': True}}
>>> id(dict1)
140641196228824
>>> id(dict2)
140641197662072
>>> id(dict1["key2"])
140641178056312
>>> id(dict2["key2"])
140641197662000
>>> dict2["key2"]["mutable"] = False
>>> dict2
{'key1': 'value1', 'key2': {'mutable': False}}
>>> dict1
{'key1': 'value1', 'key2': {'mutable': True}}

正如您所看到的,id是不同的,这意味着dict2完全是一个新的dict,其中包含dict1中的所有值。

当你想改变任何可变值而不影响原始字典时,需要使用Deepcopy。如果不是,你可以使用浅拷贝。Deepcopy很慢,因为它递归地复制原始字典中的任何嵌套值,还需要额外的内存。

当你赋值dict2 = dict1时,你并没有创建一个dict1的副本,结果是dict2只是dict1的另一个名字。

要复制像字典这样的可变类型,使用copy模块的copy / deepcopy。

import copy

dict2 = copy.deepcopy(dict1)

正如其他人解释的那样,内置字典不能做你想做的事情。但是在Python2(可能还有python3)中,你可以很容易地创建一个ValueDict类,使用=进行复制,这样你就可以确保原始的类不会改变。

class ValueDict(dict):

    def __ilshift__(self, args):
        result = ValueDict(self)
        if isinstance(args, dict):
            dict.update(result, args)
        else:
            dict.__setitem__(result, *args)
        return result # Pythonic LVALUE modification

    def __irshift__(self, args):
        result = ValueDict(self)
        dict.__delitem__(result, args)
        return result # Pythonic LVALUE modification

    def __setitem__(self, k, v):
        raise AttributeError, \
            "Use \"value_dict<<='%s', ...\" instead of \"d[%s] = ...\"" % (k,k)

    def __delitem__(self, k):
        raise AttributeError, \
            "Use \"value_dict>>='%s'\" instead of \"del d[%s]" % (k,k)

    def update(self, d2):
        raise AttributeError, \
            "Use \"value_dict<<=dict2\" instead of \"value_dict.update(dict2)\""


# test
d = ValueDict()

d <<='apples', 5
d <<='pears', 8
print "d =", d

e = d
e <<='bananas', 1
print "e =", e
print "d =", d

d >>='pears'
print "d =", d
d <<={'blueberries': 2, 'watermelons': 315}
print "d =", d
print "e =", e
print "e['bananas'] =", e['bananas']


# result
d = {'apples': 5, 'pears': 8}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
d = {'apples': 5, 'pears': 8}
d = {'apples': 5}
d = {'watermelons': 315, 'blueberries': 2, 'apples': 5}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
e['bananas'] = 1

# e[0]=3
# would give:
# AttributeError: Use "value_dict<<='0', ..." instead of "d[0] = ..."

请参考这里讨论的左值修改模式:Python 2.7 -用于左值修改的干净语法。关键的观察是,str和int在Python中表现为值(即使它们实际上是不可变的对象)。在观察的同时,请注意str或int并没有什么神奇的特殊之处。dict可以以几乎相同的方式使用,我可以想到ValueDict有意义的许多情况。

Python中的赋值语句不复制对象,而是在目标和对象之间创建绑定。

因此,dict2 = dict1,结果在dict2和dict1引用的对象之间产生另一个绑定。

如果你想复制字典,你可以使用copy模块。 复制模块有两个接口:

copy.copy(x)
Return a shallow copy of x.

copy.deepcopy(x)
Return a deep copy of x.

浅复制和深复制之间的区别只与复合对象(包含其他对象的对象,如列表或类实例)相关:

浅拷贝构造一个新的复合对象,然后(在可能的范围内)将对原始对象的引用插入其中。

深度复制构造一个新的复合对象,然后递归地将原始对象中的对象的副本插入其中。

例如,在python 2.7.9中:

>>> import copy
>>> a = [1,2,3,4,['a', 'b']]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> a.append(5)
>>> a[4].append('c')

结果是:

>>> a
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> b
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> c
[1, 2, 3, 4, ['a', 'b', 'c']]
>>> d
[1, 2, 3, 4, ['a', 'b']]