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
也就是其中之一赋值的时候,标签origin
和eq
都没有动,还是贴在物品上,只是改个名。因此origin
和eq
的值都变了。
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,对于一个复杂对象的子对象都不会完全复制。
-
什么是复杂对象的子对象。
比如序列里的嵌套子序列,字典里的嵌套序列的等都是复杂的子对象,
-
浅拷贝的概念图如下:
- 上面例子中cop1[1, 2]和origin都指向的同一个list。这里的复杂对象的子对象就是[3, 4]
- 对于复杂对象的子对象,python会将他们放在一个公共区域保存起来,在公共区域,要是被引用改变了的话,其值也会被改变。
- 因此,只要我们改变[3, 4]中的元素,也会被我改成'haha'了。
深拷贝
- 深拷贝的概念图如下:
- 如上图我们可以知道,深拷贝的时候会将复杂的对象的子对象分为copy成一个单独的个体出来,不再是公共领域,这样一来,改变origin中的值,深拷贝中的值也不会被改变了。
网友评论