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

Mad*_*ist 737 python dictionary reference python-3.x

有人可以向我解释一下吗?这对我没有任何意义.

我将字典复制到另一个字典中并编辑第二个字典,然后更改两个字典 为什么会这样?

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}
Run Code Online (Sandbox Code Playgroud)

Mik*_*ham 784

Python 从不隐式复制对象.当你设置时dict2 = dict1,你正在使它们引用相同的dict对象,所以当你改变它时,对它的所有引用都会继续引用当前状态的对象.

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

dict2 = dict(dict1)
Run Code Online (Sandbox Code Playgroud)

要么

dict2 = dict1.copy()
Run Code Online (Sandbox Code Playgroud)

  • 另请注意,dict.copy()很浅,如果有嵌套列表/ etc,则会对这两者应用更改.IIRC.Deepcopy会避免这种情况. (244认同)
  • 未经证实的言论,如"深拷贝被认为是有害的"是没有用的.在其他条件相同的情况下,浅复制复杂的数据结构*显着*更可能产生意外的边缘情况问题而不是深度复制相同的结构.修改修改原始对象的副本不是副本; 这是一个错误.因此,大多数用例绝对*应该*调用`copy.deepcopy()`而不是`dict()`或`dict.copy()`.[Imran](/sf/users/132821/)的[简明回答](/sf/answers/172616601/)位于理智的右侧,与此答案不同. (30认同)
  • 可能更好的说"dict2和dict1指向**相同的**字典",你不是在改变dict1或dict2,而是指向它们. (25认同)
  • @danielkullmann,我想你可能会根据你处理其他语言的工作方式对Python产生误解.在Python中,a)没有"原始数据类型"的概念.`int`,`float`和`bool`情况是真实的Python对象,和b)这些类型的对象,当你通过他们,而不是在肯定语义Python的水平,甚至还不如一个实现细节中不会隐式复制CPython的. (17认同)
  • python从不隐式复制对象是不正确的.原始数据类型(如int,float和bool)也被视为对象(只需执行`dir(1)`来查看),但它们是隐式复制的. (15认同)
  • @MikeGraham:你能指出一些深度复制的静音错误的例子吗?与子对象无法正确复制有什么关系? (6认同)
  • 不,我说它是一个指针的东西,但后来我试图重写它,而不是听起来像一个古老的C++程序员告诉人们新语言很糟糕.这也是人们学习c ++的常见问题.你的答案是完美的,我已经+ 1了. (4认同)
  • 我建议尽量忘记存在`copy`模块. (2认同)
  • 请注意,使用`dict1.copy()`将确保在必要时您的结果类型与`dict1`相同.这可能很重要,例如使用`OrderedDict`时 (2认同)
  • 如果字典是多层的,并且您想要一个完全独立的副本,请使用deepcopy ...否则,dcopy = d.copy();。dcopy ['top'] ['nested'] ['bottom'] = 1也会更改d ['top'] ['nested'] ['bottom']引用的对象。当尝试将一定范围的偏移量应用于批处理分析到循环内数据结构中的值时,这使我很烦……我的偏移量正在累积地起作用,因为它们影响原始和副本。 (2认同)
  • 使用`copy.deepcopy`时要非常小心 - 引入静默错误非常容易.它绝大多数时间都可以而且应该避免. (2认同)
  • @MikeGraham你能提到为什么你说复制词典是"罕见的"吗? (2认同)
  • @ user1938107,你经常用你想要的差异构建一个dict,而不是复制dict然后改变它. (2认同)
  • 深拷贝怎么办?如果我想复制词典字典怎么办?在这种情况下,您需要从导入副本中获取“ dict2 = copy.deepcopy(dict1)”。 (2认同)
  • @MikeGraham:与 naught101 相同的问题:使用 deepcopy 可能会出现哪些错误? (2认同)
  • @ user2357112:哈哈...这似乎是当之无愧的运行时错误。我看不出这样的结局会如何,或者以:P开头是个好主意 (2认同)
  • @CecilCurry:正确的答案是理解Python对象模型的工作原理.我建议人们阅读[Ned Batchelder关于Python名称的文章](https://nedbatchelder.com/text/names.html),这样他们就自己需要什么样的复制做出明智的决定.有时答案根本不是复制,也不是使用浅复制的循环. (2认同)
  • 这个答案是不正确的。“深复制”的答案是正确的。使用浅拷贝并不是解决问题的有效方法 (2认同)
  • @Hugo:对于OP的情况来说,这是一个完全足够的解决方案,因为键和值都是不可变类型;深层复制不会以任何方式改进它(它甚至不会改变行为;“str”的“深层复制”是恒等函数,它实际上并不复制它,所以浅层和深层复制是*相同的* 最终结果在这里)。我 100% 同意,当值可能是可变类型时,“deepcopy”是更安全的解决方案,但当它对于 OP 的特定用例来说 100% 正确时,不要认为这个答案不正确。 (2认同)
  • 基于所提出问题的原始答案不仅是错误的,而且是危险的。假设您有一个模块级字典(实际上是全局的)作为模板,您希望在以不同方式重复调用时在 1 个或多个函数中对其进行修改。正如塞西尔·库里和其他人所说,深度复制是唯一的方法。11 年后,我建议删除这个答案。下面还有很多答案,仍然可以让人们在做假定的副本时参考原始词典。如果有人很着急,他们可能不会阅读这些评论。 (2认同)

Imr*_*ran 560

当你指定时dict2 = dict1,你没有复制dict1,它导致dict2只是另一个名称dict1.

要复制的可变类型如字典,使用copy/ deepcopy中的copy模块.

import copy

dict2 = copy.deepcopy(dict1)
Run Code Online (Sandbox Code Playgroud)

  • 对于我曾经使用的任何字典,深度复制是我需要的...我只是因为一个错误而丢失了几个小时,因为我没有得到嵌套字典的完整副本而且我对嵌套条目的更改影响了原始. (75认同)
  • **这应该是公认的答案.**当前[已接受的答案](/sf/answers/172615271/)的评论部分中嵌入了未经证实的"深层复制被视为有害"的言论,公然邀请复制嵌套字典(例如此处记录的那些字典)时同步会遇到困难,因此应该受到质疑. (21认同)
  • 这实际上应该被标记为正确的答案; 这个答案是通用的,它也适用于字典词典. (8认同)
  • 同样在这里.deepcopy()可以解决问题.通过向原始事件的"副本"添加时间戳,将我嵌套的dicts搞乱到旋转缓存中.谢谢! (7认同)

gpa*_*nda 135

>>> 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}}
Run Code Online (Sandbox Code Playgroud)

  • 只是为了澄清:`w = copy.deepcopy(x)`是关键线. (24认同)
  • 这应该是正确的答案,因为它不会显式循环dict并且可以用于其他主要结构. (2认同)

Vkr*_*ddy 67

深入而简单的记忆方法:

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

除了使用dict2 = dict1的,你应该用复制(浅复制)和deepcopy的从Python的方法复制模块从dict1单独dict2。

正确的方法是:

>>> 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
>>> 
Run Code Online (Sandbox Code Playgroud)

如您所见,dict1 和 dict2的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
Run Code Online (Sandbox Code Playgroud)

您可以看到,即使我们为 dict1 应用了 copy,但在 dict2 和 dict1 上 mutable 的值都更改为 false,即使我们只在 dict2 上更改它。这是因为我们更改了 dict1 的可变 dict 部分的值。当我们在 dict 上应用副本时,它只会做一个浅拷贝,这意味着它将所有不可变值复制到新的 dict 中,并且不会复制可变值,但会引用它们。

最终的解决方案是对 dict1 进行 deepycopy 以完全创建一个新的 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}}
Run Code Online (Sandbox Code Playgroud)

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

如果您想在不影响原始字典的情况下更改任何可变值,则需要使用 Deepcopy。如果没有,您可以使用浅拷贝。Deepcopy 很慢,因为它递归地复制原始字典中的任何嵌套值,并且还需要额外的内存。


Pab*_*rre 59

在python 3.5+上,使用**unpackaging运算符可以更轻松地实现浅拷贝.由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?!'}
Run Code Online (Sandbox Code Playgroud)

**将字典解压缩到一个新的字典中,然后分配给dict2.

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

>>>id(dict1)
 178192816

>>>id(dict2)
 178192600
Run Code Online (Sandbox Code Playgroud)

如果需要深层复制,那么copy.deepcopy()仍然是可行的方法.

  • 这看起来非常像C++中的指针.很高兴完成任务,但可读性明智我倾向于不喜欢这种类型的运算符. (3认同)
  • 请注意,它仅执行浅表复制。 (2认同)
  • 如果要创建带有一些香料的副本,则很有用:dict2 = {** dict1,'key3':'value3'}` (2认同)

AKa*_*ala 45

最好的和最简单的方法创建一个副本一个的字典中都Python的2.7和3是...

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

1.使用dict()方法,而不是生成指向现有dict的引用.

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'}
Run Code Online (Sandbox Code Playgroud)

2.使用python字典的内置update()方法.

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'}
Run Code Online (Sandbox Code Playgroud)

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

使用内置复制模块,该模块提供通用的浅层和深层复制操作.该模块存在于Python 2.7和3中.*

import copy

my_dict2 = copy.deepcopy(my_dict1)
Run Code Online (Sandbox Code Playgroud)

  • 我相信`dict()`创建一个浅拷贝而不是深拷贝.这意味着如果你有一个嵌套的`dict`那么外部的'dict`将是一个副本,但内部的dict将是对原始内部dict的引用. (6认同)

Das*_*hes 35

您也可以使用词典理解来创建一个新词典.这可以避免导入副本.

dout = dict((k,v) for k,v in mydict.items())
Run Code Online (Sandbox Code Playgroud)

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

dout = {k:v for k,v in mydict.items()}
Run Code Online (Sandbox Code Playgroud)

但是对于向后兼容,顶级方法更好.

  • 请注意,此方法不执行深层复制,如果您需要浅复制而无需控制要复制的键,`d2 = dict.copy(d1)`也不需要任何导入. (13认同)
  • 如果您想要更好地控制复制的方式和内容,这将特别有用.+1 (4认同)

d4r*_*rty 19

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

shallow_copy_of_other_dict = {**other_dict}.

现在你将有一个"浅"的副本other_dict.

适用于您的示例:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = {**dict1}
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>>
Run Code Online (Sandbox Code Playgroud)

指针:浅层和深层复制品之间的区别

  • 我正在尝试这个,但遇到了麻烦。这仅适用于 python 3.5 及更高版本。https://www.python.org/dev/peps/pep-0448/ (2认同)

小智 17

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

所以,dict2 = dict1它会导致dict2dict1引用的对象之间的另一个绑定.

如果你想复制一个字典,你可以使用copy module.复制模块有两个界面:

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

copy.deepcopy(x)
Return a deep copy of x.
Run Code Online (Sandbox Code Playgroud)

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

浅拷贝构造新化合物对象,然后(在可能的范围)插入到其在原始找到的对象的引用.

深层副本构造新化合物的对象,然后,递归地,插入拷贝到它在原始找到的对象的.

例如,在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')
Run Code Online (Sandbox Code Playgroud)

结果是:

>>> 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']]
Run Code Online (Sandbox Code Playgroud)


Fre*_*abe 10

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

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


Cra*_*een 9

最初,这让我很困惑,因为我来自C背景.

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

但在Python中,变量更像是指向对象的指针.因此,将一个变量分配给另一个变量不会产生副本,它只会使该变量名称指向同一个对象.

  • 因为Python中的所有东西都是对象!http://www.diveintopython.net/getting_to_know_python/everything_is_an_object.html(是的,这个回复已经晚了很多年,但也许对某人有用了!) (7认同)
  • python变量更像c ++引用 (5认同)
  • 我相信 Python 语言语义说没有“变量”。它们被称为“命名引用”;意味着对对象的引用是代码中的语法字符串。一个对象可以有许多对它的命名引用。像 ints 和 floats 和 str 实例这样的不可变对象每个进程只有一个实例。执行此操作时,内存中的整数 1 不会更改为 2 或同一内存地址的其他值 myvalue=1 myvalue=2 (2认同)

wis*_*sty 7

在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太多使用- 编写需要它的代码通常是不好的做法(在我看来).


小智 6

>>> dict2 = dict1
# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1
Run Code Online (Sandbox Code Playgroud)

复制Dict对象的方法有很多种,我简单的用

dict_1 = {
           'a':1,
           'b':2
         }
dict_2 = {}
dict_2.update(dict_1)
Run Code Online (Sandbox Code Playgroud)

  • `dict_2 = dict_1.copy()` 更加高效和合乎逻辑。 (13认同)
  • 请注意,如果您在 dict1 中有一个 dict,则使用 dict_1.copy() 对 dict_2 中的内部 dict 所做的更改也适用于 dict_1 中的内部 dict。在这种情况下,您应该使用 copy.deepcopy(dict_1) 代替。 (2认同)

小智 5

dict2 = dict1不复制字典.它只是给程序员第二种方式(dict2)来引用同一个字典.


Pet*_*ron 5

dict1是引用基础字典对象的符号.分配dict1dict2仅仅分配相同的参考.通过dict2符号更改键的值会更改基础对象,这也会影响dict1.这令人困惑.

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

person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26}  # does not mutate person dict
Run Code Online (Sandbox Code Playgroud)

这在语法上与以下相同:

one_year_later = dict(person, age=26)
Run Code Online (Sandbox Code Playgroud)