【问题标题】:Deep copy of dictionary created with comprehension not working用理解创建的字典的深层副本不起作用
【发布时间】:2020-11-14 02:24:44
【问题描述】:

您能否帮助我理解为什么deepcopy 不适用于以下示例中字典中的所有元素?

import copy
a = [{'id':1, 'list':[1,2,3], 'num':3}, {'id':2,' list':[4,5,6], 'num':65}]
b = {i['id']:copy.deepcopy(i) for i in a}

In [1]: print(id(a) == id(b))                                                                                                                      
Out[1]: False

In [2]: print(id(a[0]) == id(b[1]))                                                                                                                   
Out[2]: False

In [3]: print(id(a[0]['list']) == id(b[1]['list']))                                                                                                                   
Out[3]: False

In [4]: print(id(a[0]['num']) == id(b[1]['num']))                                                                                                        
Out[4]: True

特别是,与'num' 键关联的值是相同的,而'list' 键的值似乎已与deepcopy 成功复制。我猜这与所存储值的数据类型有关,有人可以指点我正确的方向吗?

谢谢!

【问题讨论】:

  • 这很正常。 “num”键是不可变的整数对象。其中一些(从 -100 到 256 左右)是在启动时预先创建的,并且总是被重复使用。

标签: python deep-copy dictionary-comprehension


【解决方案1】:

这与 dict 理解无关,但正如您所建议的,与数据类型有关:

>>> import copy
>>> x = 1
>>> copy.deepcopy(x) is x
True
>>> x = [1]
>>> copy.deepcopy(x) is x
False

@mengban 的区别是正确的:你有可变和不可变的对象(这取决于对象的类型)。不可变对象的典型示例是:整数(012...)、浮点数(3.14159),还有字符串("foo")和元组((1, 3))。可变对象的典型示例是:列表 ([1, 2, 3]) 或字典 ({'a': 1, 'b': 2})。

基本上,不可变对象的deepcopy 返回对象本身:不执行实际复制(元组有一个小技巧:我稍后会解释):

>>> x = "foo"
>>> copy.deepcopy(x) is x
True
>>> x = (1, 2)
>>> copy.deepcopy(x) is x
True

deepcopy 的可变对象会创建一个具有相同元素的对象的新实例。

这是正确的行为,因为当您获得对象o 的深层副本o2 时,约定这是您的 副本。对o 执行的任何操作都不能修改o2。如果o 是不可变的,则免费保证。但是如果o 是可变的,那么您需要创建一个具有相同内容的新实例(这意味着递归深拷贝)。

现在元组怎么了?

>>> o = ([1], [2])
>>> copy.deepcopy(o) is o
False

即使元组本身是不可变的,它的元素之一也可能是可变的。如果我给你一个o2o 值的引用(即o2 = o),你可以写o2[0].append(10) 并且我的对象o 被修改。因此deepcopy 函数在元组中查找可变对象,并决定是否需要实际的副本。


奖励:看看deepcopy implementation_deepcopy_dispatch 将类型映射到实际的复印机:

_deepcopy_dispatch = d = {}

...
d[int] = _deepcopy_atomic
d[float] = _deepcopy_atomic
d[bool] = _deepcopy_atomic
...
d[str] = _deepcopy_atomic
...
d[list] = _deepcopy_list
...
d[tuple] = _deepcopy_tuple
...
d[dict] = _deepcopy_dict
...

虽然_deepcopy_atomic 只是返回值,但_deepcopy_list_deepcopy_tuple_deepcopy_dict...通常执行深度复制。

您可以查看_deepcopy_tuple function 了解流程。基本上,深度复制每个元素,直到进行实际复制。如果制作了副本,则创建一个新的深副本元组。否则返回初始元组。

【讨论】:

    【解决方案2】:

    python 中的可变类型和不可变类型之间有很大的区别。 一般来说,Python 中的变量类型包括列表、字典和集合。不可变类型包括字符串、整数、浮点数和元组。 重新分配一个不可变类型的变量,实际上就是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象(开辟了一个新的内存地址),如果没有其他变量引用原来的对象(即引用计数为0),原来的对象会被回收。

    【讨论】:

    • “重新分配不可变类型的变量实际上是重新创建不可变类型的对象并将原始变量重新指向新创建的对象”这是绝对错误的。无论类型如何,赋值的语义总是相同的。重新分配从不创建一个新对象。它只是改变了名称所指的内容
    • 另外,变量没有类型
    猜你喜欢
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多