深复制(deepcopy)和浅复制(shadow copy)
深浅复制的区别
在面向对象语言中,一般来说浅复制是指复制对象的引用或指针,而深复制则是复制整个对象
Type * p1 = new Type();
Type *p2 = p1;//shadow copy;
// p1, p2 指向同一块内存
但是python中有所不同,
>>> a = [1, 2, 3]
>>> b = a
>>> b[0] = 1
>>> print(a)
引用的复制和C++一样, a 和 b都可以修改内存,并且可以互相影响彼此。这种现象在python语境里不叫浅复制 。
在Python中,深复制和浅复制的区别仅仅在复合对象中的区别。
浅复制(浅拷贝shaodow copy): 对象的复制只复制对象本身,不管它包含的引用指向的内容
深复制(深拷贝deep copy):对象的复制除了复制对象本身,还需要递归地把它的子对象一起复制
案例1:
写一个python版本的组合枚举程序
def _backtracking(n, k, tmp_ls, res, start):
if len(tmp_ls) == k:
res.append(tmp_ls.copy()) # 如果使用res.append(tmp_ls)不会得到正确的结果
return
for i in range(start, n + 1):
if i not in tmp_ls:
tmp_ls.append(i)
_backtracking(n, k, tmp_ls, res, i + 1)
tmp_ls.pop()
def combine(n, k):
res = []
ans = []
_backtracking(n, k, ans, res, 1)
return res
if __name__ == '__main__':
print(combine(5, 3))
这个棘手的程序在于我每次得到一个组合要把它放到结果列表中,如果临时列表tmp_ls只是复制引用,它后面的所有变更都会影响以前计算的结果。因此,如果
res.append(tmp_ls.copy())
改成
res.append(tmp_ls)
会得到一组空列表,因为tmp_ls最后pop掉什么都没有
列表的copy方法是一个shadow copy,只复制列表对象本身
>> a = [1, 2 ,3]
>> b = a.copy()
>> assert id(a) == id(b)
断言失败 # Traceback (most recent call last):
File "<input>", line 1, in <module>
AssertionError
调用 copy会返回一个新的对象。
案例2:
需要使用深复制的例子:
import copy
ref = ['a', 'b', 'c']
a = [1,2,ref]
b = copy.copy(a) # b = [1,2, ['a', 'b', 'c']], a = [1,2, ['a', 'b', 'c']]
b[0] = 100 # b = [100 ,2, ['a', 'b', 'c']], a = [1,2, ['a', 'b', 'c']] 对第一个元素的变更互不影响,因为a,b的内存各自独立
b[2][0] = 'aaa' # b = [1,2, ['aaa', 'b', 'c']]
print(a) # a = [1,2, ['aaa', 'b', 'c']] 对b中ref这个引用进行更改,会影响a中ref的值,因为它们都各自保存的是ref的引用,ref的内存还是同一份
assert id(a[2]) == id(b[2])
b = copy.deepcopy(a) # a = [1,2, ['aaa', 'b', 'c']] , b = [1,2, ['aaa', 'b', 'c']]
b[2][0] = 'xyz'
print(b) # b = [1,2, ['xyz', 'b', 'c']]
print(a) # a = [1,2, ['aaa', 'b', 'c']] a不会被b的变更影响
上面这个案例大约可以说明python的深复制和浅复制的区别
用一个草图说明如下:
image.png
Question 为什么会这样?
实际上,上面看到三层复制:一种是赋值操作,等号 ‘=’操作,一种是copy模块提供的copy方法,以及一些对类自己实现的copy方法(比如list),另一种则是copy模块实现的deepcopy方法。
一层比一层深。
为什么python不直接把浅拷贝实现为引用的复制,深拷贝实现为对象的递归复制?简而言之,copy方法的意义是什么呢?
网友评论