• Python——赋值、浅拷贝、深拷贝


     

      和很多语言一样,Python中也分为简单赋值、浅拷贝、深拷贝这几种“拷贝”方式。

      在学习过程中,一开始对浅拷贝理解很模糊。不过经过一系列的实验后,我发现对这三者的概念有了进一步的了解。

      一、赋值

      赋值算是这三种操作中最常见的了,我们通过一些例子来分析下赋值操作:

      str例

    >>> a = 'hello'
    >>> b = 'hello'
    >>> c = a
    >>> [id(x) for x in a,b,c]
    [4404120000, 4404120000, 4404120000]

      由以上指令中,我们可以发现a, b, c三者的地址是一样的。所以以上赋值的操作就相当于c = a = b = 'hello'。

      赋值是系统先给一个变量或者对象(这里是'hello')分配了内存,然后再将地址赋给a, b, c。所以它们的地址是相同的。

      list例

    >>> a = ['hello']
    >>> b = ['hello']
    >>> c = a
    >>> [id(x) for x in a,b,c]
    [4403975952, 4404095096, 4403975952]

      但是这种情况却不一样了,a和b的地址不同。为何?

      因为str是不可变的,所以同样是'hello'只有一个地址,但是list是可变的,所以必须分配两个地址。

      这时,我们希望探究以上两种情况如果修改值会如何?

      str例

    >>> a = 'world'
    >>> [id(x) for x in a,b,c]
    [4404120432, 4404120000, 4404120000]
    >>> print a, b, c
    world hello hello

      这时a的地址和值变了,但是b, c地址和值都未变。因为str的不可变性,a要重新赋值则需重新开辟内存空间,所以a的值改变,a指向的地址改变。b, c由于'hello'的不变性,不会发生改变。

      list例

    >>> a[0] = 'world'
    >>> [id(x) for x in a,b,c]
    [4403975952, 4404095096, 4403975952]
    >>> print a, b, c
    ['world'] ['hello'] ['world']

      这时a, c的值和地址均改变,但二者仍相同,b不改变。由于list的可变性,所以修改list的值不需要另外开辟空间,只需修改原地址的值。所以a, c均改变。


      在了解了以上的不同点之后,我们就能很好地分析浅拷贝和深拷贝了。

      我们均用list作为例子。

      二、浅拷贝

     
    1 >>> a = ['hello', [123, 234]]
    2 >>> b = a[:]
    3 >>> [id(x) for x in a,b]
    4 [4496003656, 4496066752]
    5 >>> [id(x) for x in a]
    6 [4496091584, 4495947536]
    7 >>> [id(x) for x in b]
    8 [4496091584, 4495947536]
     

      Line3,4可以看出a, b地址不同,这符合list是可变的,应开辟不同空间。那浅拷贝就是拷贝了一个副本吗?再看Line5 - 8,我们发现a, b中元素的地址是相同的。如果说字符串'hello'地址一致还能理解,但是第二个元素是list地址仍一致。这就说明了浅拷贝的特点,只是将容器内的元素的地址复制了一份

      接着我们尝试修改a, b中的值:

    1 >>> a[0] = 'world'
    2 >>> a[1].append(345)
    3 >>> print 'a = ', a, '
    
    ', 'b = ', b
    4 a =  ['world', [123, 234, 345]] 
    5 b =  ['hello', [123, 234, 345]]

      a中第一个元素str改变,但是b中未改变;a中第二个元素改变,b中也改变。这就符合不可变的对象修改会开辟新的空间,可变的对象修改不会开辟新空间。也进一步证明了浅拷贝仅仅是复制了容器中元素的地址


      二、深拷贝

     
    1 >>> from copy import deepcopy
    2 >>> a = ['hello', [123, 234]]
    3 >>> b = deepcopy(a)
    4 >>> [id(x) for x in a, b]
    5 [4496066824, 4496066680]
    6 >>> [id(x) for x in a]
    7 [4496091584, 4496067040]
    8 >>> [id(x) for x in b]
    9 [4496091584, 4496371792]
     

      深拷贝后,可以发现a, b地址以及a, b中元素地址均不同。这才是完全拷贝了一个副本

      修改a的值后:

    1 >>> a[0] = 'world'
    2 >>> a[1].append(345)
    3 >>> print 'a = ', a, '
    
    ', 'b = ', b
    4 a =  ['world', [123, 234, 345]] 
    5 b =  ['hello', [123, 234]]

      从Line4,5中可以发现仅仅a修改了,b没有任何修改。因为b是一个完全的副本,元素地址均与a不同,a修改,b不受影响


      总结:

      1. 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址(旧瓶装旧酒)。

      2. 浅拷贝是在另一块地址中创建一个新的变量或容器,但是容器内的元素的地址均是源对象的元素的地址的拷贝。也就是说新的容器中指向了旧的元素(新瓶装旧酒)。

      3. 深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说(新瓶装新酒)。

     
  • 相关阅读:
    二项堆(三)之 Java的实现
    二项堆(二)之 C++的实现
    二项堆(一)之 图文解析 和 C语言的实现
    斜堆(三)之 Java的实现
    斜堆(二)之 C++的实现
    斜堆(一)之 C语言的实现
    左倾堆(三)之 Java的实现
    左倾堆(二)之 C++的实现
    左倾堆(一)之 图文解析 和 C语言的实现
    二叉堆(三)之 Java的实现
  • 原文地址:https://www.cnblogs.com/PythonMrChu/p/10673507.html
Copyright © 2020-2023  润新知