美文网首页
Python 浅拷贝、深拷贝详细图文解析

Python 浅拷贝、深拷贝详细图文解析

作者: 时间煮菜 | 来源:发表于2020-03-03 22:35 被阅读0次

Python一大坑

今天在写算法题,到了01背包问题,我用了一个二维数组,在创建数组时,我用了这样的方法(自以为很简单,结果坑了一天)

In [69]: result = [[0] * 7] * 4

In [70]: result
Out[70]:
[[0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]]

我开始还很困惑,以为是我的算法有问题,还是太小白了,

后来才反应过来,可能是创建数组的时候发生了问题。

我进行了尝试,在修改值的时候,发现出现了这样的问题:

In [60]: result[0][1] = 3

In [61]: result
Out[61]:
[[0, 3, 0, 0, 0, 0, 0],
 [0, 3, 0, 0, 0, 0, 0],
 [0, 3, 0, 0, 0, 0, 0],
 [0, 3, 0, 0, 0, 0, 0]]

我只是给result[0][1]赋了值=3,怎么整个第二列都变成了3。

后经查阅资料,

list * n—>n shallow copies of list concatenated, n个list的浅拷贝的连接

In [64]:  lists = [[]] * 3

In [65]: lists
Out[65]: [[], [], []]

In [66]: lists[0].append(3)

In [67]: list
Out[67]: list

In [68]: lists
Out[68]: [[3], [3], [3]]

这里我就困惑了,到底什么是浅拷贝。

首先明确几点:

  • 什么是赋值
  • 什么是浅拷贝
  • 什么是深拷贝

赋值

赋值并不会产生一个新的独立的对象,只是给原有的数据打上一个新标签。当其中的一个标签被改变时,这个数据就会发生变化,另一个标签也会随之改变。

先看一段代码:

In [141]: origin = [1, 2, 3] # [1, 2, 3]被贴上了标签origin

In [142]: eq = origin  # origin赋值给eq,[1, 2, 3]又被贴上了标签origin

In [143]: eq
Out[143]: [1, 2, 3]

In [144]: origin = [4, 5, 6]  # origin整体发生变化,相当于标签origin从[1,2,3]上撕了下来,贴给了[4, 5, 6]

In [145]: origin
Out[145]: [4, 5, 6]

In [146]: eq  # eq并没有发生变化,标签eq没有被撕下来
Out[146]: [1, 2, 3]
    
    

In [150]: origin = [1, 2, 3]

In [151]: eq = origin

In [152]: origin[0] = 4  # 只有origin[0]发生变化

In [153]: origin
Out[153]: [4, 2, 3]

In [154]: eq
Out[154]: [4, 2, 3]  # eq[0]也发生变化
  • [1, 2, 3] 相当于一个物体球,起初被贴上了标签origin。
  • 这里对origin的整体赋值的时候,标签origin撕了下来贴在了物体三角[4, 5, 6]上。但是eq的标签没有撕掉,还是贴在了物体球上。
  • 而对origin[0]=4也就是其中之一赋值的时候,标签origineq都没有动,还是贴在物品上,只是改个名。因此origineq的值都变了。
In [155]: origin = [1, 2, 3]

In [156]: eq = origin

In [157]: origin == eq
Out[157]: True

In [158]: origin is eq
Out[158]: True

他们的值和引用相同。

参考文章python中copy()和deepcopy()详解

浅拷贝

先上代码:

In [1]: import copy

In [2]: origin = [1, 2, [3, 4]]  # 这里是不能是一维list,后面会详细说明

In [3]: cop1 = copy.copy(origin)

In [4]: cop2 = copy.deepcopy(origin)

In [5]: cop1 == cop2
Out[5]: True

In [6]: cop1 is cop2
Out[6]: False
#cop1 和 cop2 看上去相同,但已不再是同一个object

In [7]: origin[2][0] = "haha"  # 修改origin[2][0]

In [8]: origin
Out[8]: [1, 2, ['haha', 4]]

In [9]: cop1  # 浅拷贝的元素变了
Out[9]: [1, 2, ['haha', 4]]

In [10]: cop2  # 深拷贝的元素没变
Out[10]: [1, 2, [3, 4]]

以上我们分为进行了,shallow copy(浅拷贝)-> cop1

和deep copy(深拷贝)-> cop2

为什么浅拷贝的元素变了呢,如果有兴趣,你会发现在一维数组中,浅拷贝的元素也不会变

In [11]: origin = [1, 2, 3, 4]

In [12]: cop1 = copy.copy(origin)

In [13]: cop2 = copy.deepcopy(origin)

In [14]: origin[0] = "haha"

In [15]: origin
Out[15]: ['haha', 2, 3, 4]

In [16]: cop1
Out[16]: [1, 2, 3, 4]

In [17]: cop2
Out[17]: [1, 2, 3, 4]
  • 这是由于,无论是shallow copy(浅拷贝)还是deep copy(深拷贝),只要是copy,对于一个复杂对象的子对象都不会完全复制。

  • 什么是复杂对象的子对象。

    比如序列里的嵌套子序列,字典里的嵌套序列的等都是复杂的子对象,

  • 浅拷贝的概念图如下:

image
  • 上面例子中cop1[1, 2]和origin都指向的同一个list。这里的复杂对象的子对象就是[3, 4]
  • 对于复杂对象的子对象,python会将他们放在一个公共区域保存起来,在公共区域,要是被引用改变了的话,其值也会被改变。
  • 因此,只要我们改变[3, 4]中的元素,也会被我改成'haha'了。

深拷贝

  • 深拷贝的概念图如下:
image
  • 如上图我们可以知道,深拷贝的时候会将复杂的对象的子对象分为copy成一个单独的个体出来,不再是公共领域,这样一来,改变origin中的值,深拷贝中的值也不会被改变了。

相关文章

网友评论

      本文标题:Python 浅拷贝、深拷贝详细图文解析

      本文链接:https://www.haomeiwen.com/subject/quypkhtx.html