深拷贝和浅拷贝

2021-09-07  本文已影响0人  东方胖

深复制(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方法的意义是什么呢?

上一篇下一篇

猜你喜欢

热点阅读