How to copy a dictionary and only edit the copy
有人能给我解释一下吗?这对我来说毫无意义。
我把一本字典复制到另一本,然后编辑第二本,两本都变了。为什么会这样?
1 2 3 4 5 6 7 | >>> dict1 = {"key1":"value1","key2":"value2"} >>> dict2 = dict1 >>> dict2 {'key2': 'value2', 'key1': 'value1'} >>> dict2["key2"] ="WHY?!" >>> dict1 {'key2': 'WHY?!', 'key1': 'value1'} |
python从不隐式复制对象。设置
如果你想抄写这篇短文(这很少见),你必须用
1 | dict2 = dict(dict1) |
或
1 | dict2 = dict1.copy() |
当您分配
要复制字典等可变类型,请使用
1 2 3 | import copy dict2 = copy.deepcopy(dict1) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | >>> x={'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3} >>> u=x.copy() >>> v=dict(x) >>> import copy >>> w=copy.deepcopy(x) >>> x['a']=10 >>> x {'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> u {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> v {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> w {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> x['b']['m']=40 >>> x {'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}} >>> u {'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}} >>> v {'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}} >>> w {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} |
在Python3.5+上,有一种更简单的方法可以通过使用**解包操作符来实现浅拷贝。由PEP 448定义。
1 2 3 4 5 6 7 8 9 | >>>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?!'} |
**将字典解包到新字典中,然后将其分配给dict2。
我们还可以确认每个字典都有一个不同的ID。
1 2 3 4 5 | >>>id(dict1) 178192816 >>>id(dict2) 178192600 |
如果需要深度复制,那么copy.deepcopy()仍然是一种方法。
你也可以做一本新的字典来理解字典。这样可以避免导入副本。
1 | dout = dict((k,v) for k,v in mydict.items()) |
当然,在python>=2.7中,可以执行以下操作:
1 | dout = {k:v for k,v in mydict.items()} |
但是对于向后兼容,top方法更好。
在python2.7和3中创建dict副本的最好和最简单的方法是…
要创建简单(单级)词典的副本,请执行以下操作:
1。使用dict()方法,而不是生成指向现有dict的引用。
1 2 3 4 5 6 7 8 9 10 11 | 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_dict1 my_dict1["name"] ="Emrit" print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'} print(my_dict2) # {'message':'Hello Python'} |
2。使用python字典的内置update()方法。
1 2 3 4 5 6 7 8 | my_dict2 = dict() my_dict2.update(my_dict1) print(my_dict2) # {'message':'Hello Python'} # Made changes in my_dict1 my_dict1["name"] ="Emrit" print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'} print(my_dict2) # {'message':'Hello Python'} |
要创建嵌套或复杂字典的副本,请执行以下操作:
使用内置的复制模块,该模块提供一般的浅层和深层复制操作。此模块同时存在于python 2.7和3中。*
1 2 3 | import copy my_dict2 = copy.deepcopy(my_dict1) |
python中的赋值语句不复制对象,它们在目标和对象之间创建绑定。
因此,
如果你想复制一个口述,你可以使用
1 2 3 4 5 | copy.copy(x) Return a shallow copy of x. copy.deepcopy(x) Return a deep copy of x. |
浅复制和深复制之间的区别仅与复合对象(包含其他对象的对象,如列表或类实例)相关:
一个浅拷贝构造一个新的复合对象,然后(在可能的范围内)向其中插入对原始对象的引用。
深度复制构造新的复合对象,然后递归地将原始对象的副本插入其中。
例如,在python 2.7.9中:
1 2 3 4 5 6 7 | >>> 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') |
结果是:
1 2 3 4 5 6 7 8 | >>> 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']] |
除了提供的其他解决方案外,您还可以使用
现在您将有一个"浅"版的
应用于您的示例:
1 2 3 4 5 6 7 8 | >>> dict1 = {"key1":"value1","key2":"value2"} >>> dict2 = {**dict1} >>> dict2 {'key1': 'value1', 'key2': 'value2'} >>> dict2["key2"] ="WHY?!" >>> dict1 {'key1': 'value1', 'key2': 'value2'} >>> |
指针:浅拷贝和深拷贝的区别
通过使用附加关键字参数调用
1 2 3 4 5 6 | >>> dict1 = {"key1":"value1","key2":"value2"} >>> dict2 = dict(dict1, key2="WHY?!") >>> dict1 {'key2': 'value2', 'key1': 'value1'} >>> dict2 {'key2': 'WHY?!', 'key1': 'value1'} |
最初,这也让我困惑,因为我来自C背景。
在C语言中,变量是具有定义类型的内存中的一个位置。分配给变量会将数据复制到变量的内存位置。
但在Python中,变量的作用更像是指向对象的指针。因此,将一个变量分配给另一个变量并不意味着复制,它只是使该变量名指向同一个对象。
python中的每个变量(比如
如果设置
你可以查一下:
你想要
我不经常使用
与引用相比,对不可变值进行推理要容易得多,因此尽可能进行复制:
1 2 | person = {'name': 'Mary', 'age': 25} one_year_later = {**person, 'age': 26} # does not mutate person dict |
这在语法上与以下内容相同:
1 | one_year_later = dict(person, age=26) |
1 2 | >>> 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对象有很多方法,我只是使用
1 2 3 4 5 6 | dict_1 = { 'a':1, 'b':2 } dict_2 = {} dict_2.update(dict_1) |
正如其他人所解释的,内置的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 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] = ..." |
请参考这里讨论的lvalue修改模式:python2.7-clean语法进行lvalue修改。关键的观察是,
因为python使用reference,所以当您执行dict2=dict1时,会传递一个对dict2的引用,这与dict1相同。所以,当你改变dict1或dict2时,你会改变一个引用,两个dict都会改变。对不起,如果我把英语搞错了。
很好的解释,我想添加一个最简单的规则,您可以在考虑用
不可变数据类型:字符串(字符的元组)、元组
可变数据类型:列表、数组、字典
因为dict2=dict1,dict2保存对dict1的引用。dict1和dict2都指向内存中的相同位置。这只是在Python中处理可变对象时的一个正常情况。在Python中使用可变对象时,必须小心,因为很难调试。比如下面的例子。
1 2 3 4 5 6 7 8 | my_users = { 'ids':[1,2], 'blocked_ids':[5,6,7] } ids = my_users.get('ids') ids.extend(my_users.get('blocked_ids')) #all_ids print ids#output:[1, 2, 5, 6, 7] print my_users #output:{'blocked_ids': [5, 6, 7], 'ids': [1, 2, 5, 6, 7]} |
这个示例的目的是获取所有用户ID,包括被阻止的ID。这是我们从ids变量得到的,但我们也无意中更新了我的用户的值。当您使用阻止的_id扩展ID时,我的_用户会得到更新,因为ID指向我的_用户。
您可以直接使用:
1 | dict2 = eval(repr(dict1)) |
其中,对象dict2是dict1的独立副本,因此可以修改dict2而不影响dict1。
这适用于任何类型的对象。