美文网首页
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