zoukankan      html  css  js  c++  java
  • 深度解析:python之浅拷贝与深拷贝

    深度解析python之浅拷贝与深拷贝

    本文包括知识点:

    1.copy与deepcopy

    2.可变类型与不可变类型


    1.copy与deepcopy

    在日常python编码过程中,经常会遇见变量的赋值。这一部分会用代码+图解的形式解释=,copy,deepcopy的区别。

    1. 直接赋值

    Bill = ["Gates", 50, ["Python", "C#", "JavaScript"]]
    Jack = Bill
    print id(Bill)
    print Bill
    print [id(ele) for ele in Bill]
    print id(Jack)
    print Jack
    print [id(ele) for ele in Jack]
    print Jack is Bill
    
    #output:
    97597256
    ['Gates', 50, ['Python', 'C#', 'JavaScript']]
    [97625072L, 34047944L, 97620808L]
    97597256
    ['Gates', 50, ['Python', 'C#', 'JavaScript']]
    [97625072L, 34047944L, 97620808L]
    True
    
    Bill[0] = "Mark"
    Bill[2].append("Scala")
    print id(Bill)
    print Bill
    print [id(ele) for ele in Bill]
    print id(Jack)
    print Jack
    print [id(ele) for ele in Jack]
    
    #output:
    97262024
    ['Mark', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
    [97615224L, 34047944L, 97596616L]
    97262024
    ['Mark', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
    [97615224L, 34047944L, 97596616L]
    

    filelist
    结论
    这说明采用直接赋值的方式,变量Bill和Jack的地址完全相同,同时他们的内部元素的地址也完全相同,Bill的元素修改Jack上也成立。需要注意的是strint等原子类型是不可变类型,在不改变地址的前提下,无法改变值,所以Bill[0]的值发生了变换,且分配了一个新的地址。而list ,dict等容器类型式可变类型,在地址不变的前提下,可以更改值,所以Bill[2]的值发生了变化,但是地址不变。

    2. 浅拷贝

    再来看下面一段代码:

    import copy
     
    Bill = ["Gates", 50, ["Python", "C#", "JavaScript"]]
    Jack = copy.copy(Bill)
    print id(Bill)
    print Bill
    print [id(ele) for ele in Bill]
    print id(Jack)
    print Jack
    print [id(ele) for ele in Jack]
    print Jack is Bill
    
    #output:
    97263496
    ['Gates', 50, ['Python', 'C#', 'JavaScript']]
    [97625072L, 34047944L, 97262024L]
    98156168
    ['Gates', 50, ['Python', 'C#', 'JavaScript']]
    [97625072L, 34047944L, 97262024L]
    False
    
    Bill[0] = "Mark"
    Bill[2].append("Scala")
    print id(Bill)
    print Bill
    print [id(ele) for ele in Bill]
    print id(Jack)
    print Jack
    print [id(ele) for ele in Jack]
    
    #output:
    97263496
    ['Mark', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
    [97613904L, 34047944L, 97262024L]
    98156168
    ['Gates', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
    [97625072L, 34047944L, 97262024L]
    

    filelist

    这说明,浅拷贝的变量Jack与Bill的地址不同,即 Jack is not Bill, 但是它俩的元素地址相同,即Jack[i] is Bill[i]。
    浅拷贝之后,对原变量元素进行赋值。Bill[0]与Bill[-1]都发生了变化,且由于Bill[0]是不可变类型,分配了新的地址,而浅拷贝对象Jack[0]还指向原地址,所以Jack[0]的值还是原来的值。而Bill[-1]是原地变化,所以Bill[-1]也进行了相应的变化。

    3.深拷贝

    老惯例,先上代码:

    import copy
     
    Bill = ["Gates", 50, ["Python", "C#", "JavaScript"]]
    Jack = copy.deepcopy(Bill)
    print id(Bill)
    print Bill
    print [id(ele) for ele in Bill]
    print id(Jack)
    print Jack
    print [id(ele) for ele in Jack]
    print Jack is Bill
    
    #output:
    97262024
    ['Gates', 50, ['Python', 'C#', 'JavaScript']]
    [97625072L, 34047944L, 97598216L]
    97263496
    ['Gates', 50, ['Python', 'C#', 'JavaScript']]
    [97625072L, 34047944L, 97596872L]
    False
    
    
    Bill[1] = 38
    Bill[2].append("Scala")
    print id(Bill)
    print Bill
    print [id(ele) for ele in Bill]
    print id(Jack)
    print Jack
    print [id(ele) for ele in Jack]
    
    #output:
    97262024
    ['Gates', 38, ['Python', 'C#', 'JavaScript', 'Scala']]
    [97625072L, 34048232L, 97598216L]
    97263496
    ['Gates', 50, ['Python', 'C#', 'JavaScript']]
    [97625072L, 34047944L, 97596872L]
    

    filelist

    结论,可以看到,Jack是Bill的深拷贝对象,Jack is not Bill,and Jack[i] is not Bill[i],从而实现效果上,两个变量的彻底剥离。
    在初始化的时候,由于前两个元素都是不可变类型,Jack与Bill的这两个元素的地址相同。重点是对于可变类型元素,两个变量的地址是不同的,因此,不论元素类型是否可变,原对象的变化都不影响拷贝对象。

    4.总结

    = ,copy,deepcopy的区别如下:
    考虑对象A的变化对拷贝对象B的影响,应该这么考虑:
    1.A与B是否指向同一个地址(=),若是则A的任何变化都会导致B的变化。
    2.A与B指向的地址不同,若B的元素与A的元素指向地址相同(浅拷贝),且A的元素有可变类型,那么A的可变类型元素的变化会导致B的相应元素的变化。
    3.A与B指向的地址不同,且A与B的元素指向的地址也不同(深拷贝,不可变类型元素地址相同,不影响),那么A的任何类型元素的变化都不会导致B的元素变化。


    (引申知识点)

    2.可变类型与不可变类型

    什么是可变/不可变? 简言之,就是内存地址(id)和type不变的前提下,value是否是可变的。
    int ,float , str,tuple是不可变的,dict,list,set等容器类型是可变的。
    不可变类型的变量若重新赋值,就是在新的地址上创建一个新的值,并把该变量指向新的地址,若之前的变量没有其他引用的话,就直接回收旧地址。可变类型的变量可以通过append,pop,remove等操作变换值,但是地址不会变更。
    需要注意的是,tuple虽然是一种容器,但由于在初始化之后不能再更改,所以也是不可变类型。

    引申:collections包中的其他容器类型,例如OrderedDict,Iterator,Container等是可变还是不可变呢? 读者不妨自己做做实验和总结。

  • 相关阅读:
    hdu 1695 GCD 欧拉函数 + 容斥
    E. Xenia and Tree 分块 + LCA
    二分图匹配 + 构造 E. Arpa’s overnight party and Mehrdad’s silent entering
    Win系统如何修改远程桌面端口3389
    centos7下使用selenium实现文件上传
    windows下tesseract-ocr的安装及使用
    windows下pycharm输入法跟随设置
    django 外键使用select html
    xstart访问centos7
    Centos7 安装VNC实现远程桌面
  • 原文地址:https://www.cnblogs.com/bradleon/p/7374076.html
Copyright © 2011-2022 走看看