• python 中 深拷贝和浅拷贝的理解


    在总结 python 对象和引用的时候,想到其实 对于python的深拷贝和浅拷贝也可以很好对其的进行理解。

    在python中,对象的赋值的其实就是对象的引用。也就是说,当创建一个对象,然后赋给另外一个变量之后,实际上只是拷贝了这个对象的引用。

    我们先用  利用切片操作和工厂方法list方法 来阐述一下浅拷贝。

     举个栗子:

    Tom = ['Tom', ['age', 10]]
    Jack = Tom[:]  ……切片操作
    June = list(Tom)

       接下来查看一下 上述三个变量的引用:

    >>> id(Tom)
    140466700697824
    >>> id(Jack)
    140466700700488
    >>> id(June)
    140466700770192

       可以发现,三个变量分别指向了不同的对象。我们再来看一下这三个对象的内容:

    >>> Tom
    ['Tom', ['age', 10]]
    >>> Jack
    ['Tom', ['age', 10]]
    >>> June
    ['Tom', ['age', 10]]  

       显然这三个对象的内容会是一样的,因为通过上面的 切片操作 以及 工厂函数list 对Tom引用的对象进行了拷贝。接下来再进行进一步验证:

       我们对 Jack 和 June 引用的对象进行修改:

    >>> Jack[0] = 'Jack'
    >>> Jack
    ['Jack', ['age', 10]]
    >>> June[0] = 'June'
    >>> June
    ['June', ['age', 10]]

       现在我们打算对Jack的年龄进行修改:

    >>> Jack[1][1] = 20
    >>> Jack
    ['Jack', ['age', 20]]

       可以看到Jack年龄变为了20; 让我们接下来看一下Tom, June的年龄:

    >>> print Tom, Jack, June
    ['Tom', ['age', 20]] ['Jack', ['age', 20]] ['June', ['age', 20]]

       奇怪的事情发生了,我们仅仅是修改了 Jack的年龄, 但是Tom 和 June 的年龄跟着改变了, 这是为什么呢?

       这个就涉及到了python中浅拷贝的奥秘:

        我们先来看一下上面 Tom, Jack, June中内部元素的 内存地址:

    >>> for x in Tom:
    ... print id(x)
    ... 
    140704715293600  --> 'Tom'
    140704715147816  --> ['age', 20]
    >>> for x in Jack: 
    ... print id(x)
    ... 
    140704715286256 --> 'Jack'
    140704715147816 --> ['age', 20]
    >>> for x in June:
    ... print id(x)
    ... 
    140704715286352 -->'June'
    140704715147816 -->['age', 20]

        仔细观察可以看到,Tom, Jack, June 三个变量的 岁数元素['age', 20] 指向同一个 对象; 那为什么他们的 名字元素 分别指向不同的对象。这是因为,在python中的分为 可变数据对象(列表,字典) 和 不可变数据对象(整型,字符串,浮点型,元祖)。 正是因为这个原因,他们的 名字元素 为字符串,为不可变数据对象,因此开始为 Jack 和 June 重新命名的时候,实际上内存中已经创建了 Jack 和 June对象。而 岁数元素 是 可变数据对象,所以并不会在内存中创建新的对象,Tom,Jack,June的岁数元素都引用同一个对象,导致修改其中一个会让另外俩个的年龄跟着变化。

        这个就是python的浅拷贝,其仅仅是拷贝了 一个整体的对象(应该说一个对象最外面的那一层),而对于对象里面包含的元素不会进行拷贝。

    接下来,我们 利用copy中的deepcopy方法  来阐述一下 深拷贝:

        还是用上面那个栗子:

        为了让 Tom, Jack, June之间互不影响,我们用deepcopy方法对Tom进行拷贝生成 Jack 和 June:

    >>> Tom = ['Tom', ['age', 10]]
    >>> import copy
    >>> Jack = copy.deepcopy(Tom)
    >>> June = copy.deepcopy(Tom)
    >>> Jack
    ['Tom', ['age', 10]]
    >>> June
    ['Tom', ['age', 10]]
    >>> Tom
    ['Tom', ['age', 10]]

        让我们看一下Tom, Jack, June分别指向的内存地址:

    >>> print id(Tom), id(June), id(Jack)
    140707738759392 140707738799280 140707738797192

        三个内存地址不同,然后我们接着改变Jack 和 June的名字,并查看修改后它们的内部元素所指向的内存地址:

    >>> Jack[0] = 'Jack'
    >>> June[0] = 'June'
    >>> Tom
    ['Tom', ['age', 10]]
    >>> Jack
    ['Jack', ['age', 10]]
    >>> June
    ['June', ['age', 10]]
    >>> for x in Tom:
    ... print id(x)
    ... 
    140707738882976  --> 'Tom'
    140707738737192  --> ['age', 10]
    >>> for x in Jack:
    ... print id(x)
    ... 
    140707738875584  --> 'Jack'
    140707738910016  --> ['age', 10]
    >>> for x in June:
    ... print id(x)
    ... 
    140707738876640  -->'June'
    140707738910160  --> ['age', 10]

        可以清楚的看到,他们的内部元素也指向了不同的对象,说明通过deepcopy方法,对元素进行了彻底的拷贝(包括内部元素)。

    最后总结一下思路:   

    思路一:利用切片操作和工厂方法list方法拷贝就叫浅拷贝,只是拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。

    思路二:利用copy中的deepcopy方法进行拷贝就叫做深拷贝,外围和内部元素都进行了拷贝对象本身,而不是引用。

    但是对于数字,字符串和其他原子类型对象等,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。

            

        

      

        

  • 相关阅读:
    [pixhawk笔记]8-半物理仿真环境
    Python超参数自动搜索模块GridSearchCV上手
    Oriented Response Networks 阅读笔记(一)
    聚类算法评价指标学习笔记
    基于sklearn的常用分类任务指标Python实现
    使用h5py库读写超过内存的大数据
    基于MXNet使用自己的图像数据集训练网络--准备数据与撰写预处理脚本
    在Ubuntu操作系统中添加环境变量
    Jetson TK1 开发板初用体会
    一条脚本搞定OpenCV
  • 原文地址:https://www.cnblogs.com/ShaunChen/p/5658770.html
Copyright © 2020-2023  润新知