【问题标题】:Array slicing seems like it's making a deep copy?数组切片似乎是在进行深拷贝?
【发布时间】:2019-01-12 19:17:13
【问题描述】:

python 中的切片应该是浅拷贝。但是,当我运行以下命令时:

cur = [[0] * (2) for _ in xrange(2)]
cur2 = [row[:] for row in cur]
cur2[0][0] = "foo"
print(cur)
print(cur2)

我明白了:

[[0, 0], [0, 0]] # cur
[['foo', 0], [0, 0]] # cur2

这使它看起来像是一个深拷贝。

我有两个问题: 1)这里发生了什么?这是深拷贝还是浅拷贝? 2) 这种语法如何使它比copy.deepcopy 快得多?比如,是不是和python管理内存的方式有关?

【问题讨论】:

  • 你认为这些切片复制了 什么?
  • 我不确定:每行的深层副本?
  • 为什么你认为它是每一行的deep副本?您会从每行的浅拷贝中看到不同的结果吗?
  • cur 的浅拷贝将是 cur2 = cur[:]。您通过进入 cur 并复制每一行来执行深层复制。
  • @user2357112 是的 - 如果是浅拷贝,“foo”将在 cur 中。修改 cur2 也会修改 cur。

标签: python copy deep-copy shallow-copy


【解决方案1】:

您正在制作内部列表(即行)的浅拷贝,如果内部列表只是 int 对象的列表,这实际上与外部列表的深层副本相同.

您实际上已经为整数列表的特殊情况实现了深拷贝功能。

使用copy.deepcopy 会更慢,因为该函数必须调查并缓存对象的所有ID,包括int 对象。您的 sn-p 没有这样做,但在这种特殊情况下,这并不重要(注意,小的 int 对象在解释器级别缓存,它们本质上是单例,无论如何,int 对象是不可变的所以它们根本不需要被复制)。

如果您想确切了解通用深层复制中涉及的内容,请查看link to the copy module source code

【讨论】:

  • 谢谢。我不确定我是否遵循有关 int 对象的部分 - 缓存对象的 ID 完成了什么?在解释器级别缓存是什么意思(以及在什么级别缓存普通对象)?
  • @user2009020 从-2256 的小int 对象被CPython 实现缓存(这就是我所说的解释器级别)。阅读更多关于 here 的信息。 copy 模块在 Python 中使用 dict 创建自己的缓存。无论如何,您永远不必复制 int 对象,因为它们是不可变的。但同样,copy.deepcopy 事先并不知道它正在复制的 object 的任何信息,因此它会检查它已经看到的对象,以避免循环。
  • 所以我可以说 copy.deepcopy 速度较慢,因为它复制每个 int 对象,而我的问题中的代码没有?
  • @user2009020 不,它不会复制每个int 对象,对于不可变对象,__deepcopy_atomic 只是return x。但是,它会检查每个对象,并且仍然会缓存 id。
【解决方案2】:

我认为你对什么是浅拷贝和深拷贝有误解。

浅拷贝复制集合的结构(主要是内存位置),而深拷贝复制内存中的所有内容(如实际数据)。

您在这里所做的是从原始 2d 列表中形成每个子列表的副本的新列表 - 从而产生一个全新的列表,与原始列表没有联系 -因此你做了一个深拷贝

相反,如果您要这样做:cur[:],您将只是复制包含对内部列表的相同引用的外部列表 - 因此是浅复制

在您的情况下,由于所有内存位置都不同,因此更改一个元素不会影响原始元素,但如果您只是执行cur[:],那么内部行将引用内存中的相同位置,因此更改元素来自任一列表都会影响另一个列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 1970-01-01
    • 2013-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多