【问题标题】:Python Shallow and deep copiesPython 浅拷贝和深拷贝
【发布时间】:2015-01-13 12:11:05
【问题描述】:

我正在尝试执行一个 python 脚本,该脚本应该告知浅拷贝和深拷贝之间的区别。

据我了解:

  • 浅拷贝:创建一个对象并将其引用到原始文件中的内容
  • 深拷贝:创建一个对象并将在原始文件中找到的内容复制到该对象中

下面是我的程序:

a = [1,2,3]

print(id(a), id(a[0]), id(a[1]))

print("Lets check shallow copy first")

a1 = copy.copy(a)
print(id(a1), id(a1[0]), id(a1[1]))
a2 = copy.deepcopy(a)
print(id(a2), id(a2[0]), id(a2[1]))

输出:

steven@steven-Inspiron-3537:~/python-learning$ ./deepshallow.py 
(139854551376528, 31777112, 31777088)
Lets check shallow copy first
(139854551485040, 31777112, 31777088)
(139854551378616, 31777112, 31777088)

【问题讨论】:

标签: python


【解决方案1】:

您在这里看到的是,副本之间的整数是相同的,即使是深副本也是如此。这是因为整数是“不可变的”,它们的标识基于它们的值和 Python 解释器的散列设置。

试试这个

a2[0] = 24
assert a2[0] != a1[0]
assert a2[0] != a[0]
a[0] = 72
assert a[0] != a1[0]

您会发现即使是浅拷贝也不受影响。这是因为列表的所有条目都是不可变的。对于更合适的示例,请尝试将列表或字典相互嵌套并对其运行相同的测试。

【讨论】:

  • 这不太正确。例如,如果您尝试x = 1000; y = 1000; print id(x); print id(y);,您将看到xy 具有不同的id 值,即使它们引用同一个不可变对象1000,但如果您尝试使用x = 100; y = 100 代替(至少在 CPython 中)您会看到它们具有 same id 值。我认为混淆是为什么copy 的结果元素可以具有与id 相同的id,即使deepcopy 的情况也是如此,而答案只是CPython 的怪癖,即某些整数由运行时。
  • 你说得对,我将idhash 混为一谈,并进行了一些手动测试。
【解决方案2】:

在这种特殊情况下,由于列表的元素是小整数,Python 有一个内置机制来引用“相同”对象(整数),而不允许对深度复制的列表进行更改以导致更改从中复制它的列表。

这是一个像你这样的整数的例子:

In [135]: import copy

In [136]: a1 = [1, 2, 3]

In [137]: a2 = copy.copy(a1)

In [138]: a3 = copy.deepcopy(a1)

In [139]: map(id, a1)
Out[139]: [26960216, 26960192, 26960168]

In [140]: map(id, a2)
Out[140]: [26960216, 26960192, 26960168]

In [141]: map(id, a3)
Out[141]: [26960216, 26960192, 26960168]

所以在这一点上我们可以看到列表包含整数,都具有相同的 id。让我们更改深层副本中的一个元素。

In [142]: a3[0] = 1000

In [143]: map(id, a1)
Out[143]: [26960216, 26960192, 26960168]

In [144]: map(id, a2)
Out[144]: [26960216, 26960192, 26960168]

In [145]: map(id, a3)
Out[145]: [39759800, 26960192, 26960168]

现在a3 的第一个条目有一个新的 id,同时其他列表保持不变。现在让我们更改浅拷贝的第一个条目。

In [146]: a2[0] = 1000

In [147]: map(id, a1)
Out[147]: [26960216, 26960192, 26960168]

In [148]: map(id, a2)
Out[148]: [39759200, 26960192, 26960168]

In [149]: map(id, a3)
Out[149]: [39759800, 26960192, 26960168]

注意整数 1000,它作为a2a3 的第一个条目,有一个不同的 id 值。

这样做的原因是Python运行时实际上缓存了一些小整数和其他不可变对象,这意味着它们被引用的任何地方,都是对单个缓存值的引用。

这里是a source describing it

当前的实现为 -5 到 256 之间的所有整数保留一个整数对象数组,当您在该范围内创建一个 int 时,实际上您只是获取对现有对象的引用。所以应该可以改变 1 的值。我怀疑 Python 在这种情况下的行为是未定义的。 :-)

要查看deepcopycopy 具有显着不同行为的示例,我们需要deepcopycopy递归 调用将产生影响——而这缓存的小整数不会发生。

让我们尝试使用列表列表,我们将修改内容,而不是突然改变最顶层的列表元素所指的内容:

In [171]: a1 = [[1], [2], [3]]

In [172]: a2 = copy.copy(a1); a3 = copy.deepcopy(a1)

In [173]: a1
Out[173]: [[1], [2], [3]]

In [174]: a2
Out[174]: [[1], [2], [3]]

In [175]: map(id, a1)
Out[175]: [140608561277264, 140608561418040, 140608561277120]

In [176]: map(id, a2)
Out[176]: [140608561277264, 140608561418040, 140608561277120]

In [177]: a2[0][0] = 1000

In [178]: a1
Out[178]: [[1000], [2], [3]]

In [179]: a2
Out[179]: [[1000], [2], [3]]

In [180]: a3
Out[180]: [[1], [2], [3]]

In [181]: a3[1][0] = 1001

In [182]: a1
Out[182]: [[1000], [2], [3]]

In [183]: a2
Out[183]: [[1000], [2], [3]]

In [184]: a3
Out[184]: [[1], [1001], [3]]

【讨论】:

    猜你喜欢
    • 2012-04-12
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多