美文网首页
深拷贝和浅拷贝

深拷贝和浅拷贝

作者: 东方胖 | 来源:发表于2021-09-07 14:56 被阅读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方法的意义是什么呢?

    相关文章

      网友评论

          本文标题:深拷贝和浅拷贝

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