如何复制字典并仅编辑副本

我设置了dict2 = dict1。当我编辑dict2时,原始的dict1也发生了变化。为什么?

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

Python从未隐式复制对象。当您设置dict2 = dict1时,您让它们引用同一个完全相同的字典对象,因此当您突变它时,对它的所有引用都继续引用当前状态的对象。

如果你想复制字典(这很少见),你必须明确地使用

dict2 = dict(dict1)

dict2 = dict1.copy()

dict2 = dict1不复制字典。它只是为您提供了程序员引用同一字典的第二种方式(dict2)。

当您分配dict2 = dict1时,您不是在复制dict1,它导致dict2只是dict1的另一个名称。

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

import copy
dict2 = copy.deepcopy(dict1)

python中的每个变量(像dict1str__builtins__之类的东西)都是指向机器内部某些隐藏的柏拉图式“对象”的指针。

如果设置dict1 = dict2,只需将dict1指向与dict2相同的对象(或内存位置,或您喜欢的任何类比)。现在,dict1引用的对象与dict2引用的对象相同。

您可以检查:dict1 is dict2应该是True。此外,id(dict1)应该与id(dict2)相同。

你想要dict1 = copy(dict2)dict1 = deepcopy(dict2)

copydeepcopy之间的区别?deepcopy将确保dict2的元素(您是否将其指向列表?)也是副本。

我不怎么使用deepcopy——编写需要它的代码通常是糟糕的做法(在我看来)。

最初,这也让我感到困惑,因为我来自C背景。

在C中,变量是内存中具有定义类型的位置。分配给变量会将数据复制到变量的内存位置。

但是在Python中,变量更像是指向对象的指针。因此将一个变量分配给另一个变量不会复制,它只是使该变量名指向同一个对象。

您也可以只创建一个带有字典理解的新字典。这避免了导入副本。

dout = dict((k,v) for k,v in mydict.items())

当然在python>=2.7中你可以这样做:

dout = {k:v for k,v in mydict.items()}

但对于逆向计算,top方法更好。

虽然dict.copy()dict(dict1)生成一个副本,但它们只是个副本。如果您想要个副本,则需要copy.deepcopy(dict1)。一个例子:

>>> source = {'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3}>>> copy1 = x.copy()>>> copy2 = dict(x)>>> import copy>>> copy3 = copy.deepcopy(x)>>> 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#0模块文档

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

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

您可以通过使用附加关键字参数调用dict构造函数来一次性复制和编辑新构造的副本:

>>> dict1 = {"key1": "value1", "key2": "value2"}>>> dict2 = dict(dict1, key2="WHY?!")>>> dict1{'key2': 'value2', 'key1': 'value1'}>>> dict2{'key2': 'WHY?!', 'key1': 'value1'}

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

因此,dict2 = dict1,它导致dict2dict1引用的对象之间的另一个绑定。

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

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

dict1是引用底层字典对象的符号。将dict1分配给dict2只是分配相同的引用。通过dict2符号更改键的值会更改底层对象,这也会影响dict1。这令人困惑。

推理不可变值要比推理引用容易得多,所以尽可能地复制:

person = {'name': 'Mary', 'age': 25}one_year_later = {**person, 'age': 26}  # does not mutate person dict

这在语法上与:

one_year_later = dict(person, age=26)

在深度和简单的方法来记住:

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

你应该使用Python的复制模块中的复制(浅拷贝)和深度复制方法,而不是使用,来分离,从,到,从,到。

正确的做法是:

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

正如你所看到的,id是不同的,这意味着两者都指向/引用内存中的不同位置。

此解决方案适用于具有不可变值的字典,对于具有可变值的字典,这不是正确的解决方案。

例如:

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

你可以看到,即使我们为迪斯1应用了副本,在迪斯2和迪斯1上,可变的值也被更改为false,即使我们只在迪斯2上更改了它。这是因为我们更改了迪斯1的可变判决书部分的值。当我们在迪斯1上应用副本时,它只会做一个浅拷贝,这意味着它会将所有不可变的值复制到一个新的判决书中,并且不会复制可变值,但它会引用它们。

最终的解决方案是做一个深度拷贝,以完全创建一个带有复制的所有值的新字典,包括可变值。

>>>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 = eval(repr(dict1))

其中,ObjectiveCr 2是Cr 1的独立副本,因此您可以修改Cr 2而不会影响Cr 1。

这适用于任何类型的对象。

在python 3.5+上,有一种更简单的方法可以通过使用**解包运算符来实现浅拷贝。由Pep 448定义。

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

**将字典解包到一个新字典中,然后将其分配给字典2。

我们还可以确认每个字典都有一个不同的id。

>>>id(dict1)178192816
>>>id(dict2)178192600

如果需要深度复制,那么copy.deepcopy()仍然是要走的路。

正如其他人所解释的,内置的dict不能做你想要的。但是在Python2(可能还有3)中,您可以轻松创建一个与=一起复制的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)\""

# testd = ValueDict()
d <<='apples', 5d <<='pears', 8print "d =", d
e = de <<='bananas', 1print "e =", eprint "d =", d
d >>='pears'print "d =", dd <<={'blueberries': 2, 'watermelons': 315}print "d =", dprint "e =", eprint "e['bananas'] =", e['bananas']

# resultd = {'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-左值修改的干净语法。关键的观察是strint在Python中表现为值(即使它们实际上是不可变对象)。当你观察到这一点时,请同时观察到strint没有什么神奇的特别之处。dict可以以几乎相同的方式使用,我可以想到许多ValueDict有意义的情况。

python2.7和3字典创建副本的最佳和最简单的方法是…

要创建简单(单级)字典的副本:

1.使用字典方法,而不是生成指向现有字典的引用。

my_dict1 = dict()my_dict1["message"] = "Hello Python"print(my_dict1)  # {'message':'Hello Python'}
my_dict2 = dict(my_dict1)print(my_dict2)  # {'message':'Hello Python'}
# Made changes in my_dict1my_dict1["name"] = "Emrit"print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}print(my_dict2)  # {'message':'Hello Python'}

2.使用python字典的内置更新方法。

my_dict2 = dict()my_dict2.update(my_dict1)print(my_dict2)  # {'message':'Hello Python'}
# Made changes in my_dict1my_dict1["name"] = "Emrit"print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}print(my_dict2)  # {'message':'Hello Python'}

创建嵌套或复杂字典的副本:

使用内置的复制模块,它提供了通用的浅拷贝和深拷贝操作。此模块在Python 2.7和3中都存在。*

import copy
my_dict2 = copy.deepcopy(my_dict1)
>>> dict2 = dict1# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1

有很多方法可以复制DICT对象,我只是使用

dict_1 = {'a':1,'b':2}dict_2 = {}dict_2.update(dict_1)

除了提供的其他解决方案之外,您还可以使用**将字典集成到空字典中,例如:

#0.

现在您将拥有other_dict的“浅”副本。

应用于您的示例:

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

指针:浅拷贝和深拷贝之间的区别

下面的代码,这是在遵循json语法的dicts上的,比深拷贝快3倍以上

def CopyDict(dSrc):try:return json.loads(json.dumps(dSrc))except Exception as e:Logger.warning("Can't copy dict the preferred way:"+str(dSrc))return deepcopy(dSrc)

我在尝试深度复制类的字典属性时遇到了一个奇怪的行为w/o将其分配给变量

new = copy.deepcopy(my_class.a)不起作用,即修改new修改my_class.a

但是如果你做了old = my_class.a然后new = copy.deepcopy(old)它就完美地工作了,即修改new不会影响my_class.a

我不知道为什么会发生这种情况,但希望它有助于节省一些时间!:)

使用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"] = 808print(orig) # {'X2': 674.5, 'X3': 245.0}print(copy) # {'X2': 808, 'X3': 245.0}

另一种更干净的方法是使用json。请参阅下面的代码

>>> a = [{"name":"Onkar","Address": {"state":"MH","country":"India","innerAddress":{"city":"Pune"}}}]>>> b = json.dumps(a)>>> b = json.loads(b)>>> id(a)2334461105416>>> id(b)2334461105224>>> a[0]["Address"]["innerAddress"]["city"]="Nagpur">>> a[{'name': 'Onkar', 'Address': {'state': 'MH', 'country': 'India', 'innerAddress': {'city': 'Nagpur'}}}]>>> b[{'name': 'Onkar', 'Address': {'state': 'MH', 'country': 'India', 'innerAddress': {'city': 'Pune'}}}]>>> id(a[0]["Address"]["innerAddress"])2334460618376>>> id(b[0]["Address"]["innerAddress"])2334424569880

要创建另一个字典,请在同一个字典对象上json.dumps()然后json.loads()。

对于嵌套字典,不要使用dict(srcData) or srcData.copy() or {**srcData},因为如果您更改第二级或更多,它也会修改源字典

srcData = {'first': {'second': 'second Value'}}newData = dict(srcData) # srcData.copy() or {**srcData}newData['first']['second'] = 'new Second Value'
print(srcData)print(newData)
# it will print# srcData: {'first': {'second': 'new Second Value'}}# newData:{'first': {'second': 'new Second Value'}}
# but it should be# srcData: {'first': {'second': 'second Value'}}# newData:{'first': {'second': 'new Second Value'}}

深度复制的另一个选择是使用json技巧,如JavascriptJSON.parse(JSON.stringify(obj))

import json
srcData = {'first': {'second': 'second Value'}}newData = json.loads(json.dumps(srcData))newData['first']['second'] = 'new Second Value'
print(srcData)print(newData)
# srcData: {'first': {'second': 'second Value'}}# newData: {'first': {'second': 'new Second Value'}}