Python深拷贝与浅拷贝

Python深拷贝与浅拷贝

一、定义

  1. 变量:是一个系统表的元素,拥有指向对象的连接空间

  2. 对象:被分配的一块内存,存储其所代表的值

  3. 引用:是自动形成的从变量到对象的指针

    注意:类型(int类型)属于对象,不是变量

  4. 不可变对象:一旦创建就不可修改的对象,包括字符串、元组、数字

  5. 可变对象:可以修改的对象,包括列表、字典。

在Python中对象的赋值其实就是对象的引用。当创建一个对象,把它赋值给另一个变量的时候,python并没有拷贝这个对象,只是拷贝了这个对象的引用而已。

浅拷贝:拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。也就是,把对象复制一遍,但是该对象中引用的其他对象我不复制。

深拷贝:外围和内部元素都进行了拷贝对象本身,而不是引用。也就是,把对象复制一遍,并且该对象中引用的其他对象我也复制。

二、应用的范围及作用

应用的范围:

  1. 切片可以应用于:序列(列表、元组、字符串),但不能应用于字典。
  2. 深浅拷贝,既可应用序列(列表、元组、字符串),也可应用字典。

深浅拷贝的作用:

  1. 减少内存的使用。
  2. 以后在做数据的清洗、修改或者入库的时候,对原数据进行复制一份,以防数据修改之后,找不到原数据。

三、对于不可变对象的深浅拷贝

  • 不可变对象类型,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。
  • 一句话就是,不可变类型,不管是深拷贝还是浅拷贝,地址值和拷贝后的值都是一样的。
>>> a="1234567"
>>> b=a
>>> id(a)
4367619440
>>> id(b)
4367619440
>>> c=copy.copy(a)
>>> id(c)
4367619440
>>> d=copy.deepcopy(a)
>>> id(d)
4367619440

四、python的数据存储方式

Python 存储变量的方法跟其他 OOP 语言不同。它与其说是把值赋给变量,不如说是给变量建立了一个到具体值的 reference。

当在 Python 中 a = something 应该理解为给 something 贴上了一个标签 a。当再赋值给 a 的时候,就好象把 a 这个标签从原来的 something 上拿下来,贴到其他对象上,建立新的 reference。 这就解释了一些 Python 中可能遇到的诡异情况:

>>> a = [1, 2, 3]
>>> b = a
>>> a = [4, 5, 6] //赋新的值给 a
>>> a
[4, 5, 6]
>>> b
[1, 2, 3]
# a 的值改变后,b 并没有随着 a 变
 
>>> a = [1, 2, 3]
>>> b = a
>>> a[0], a[1], a[2] = 4, 5, 6 //改变原来 list 中的元素
>>> a
[4, 5, 6]
>>> b
[4, 5, 6]
# a 的值改变后,b 随着 a 变了

五、对于可变对象的深浅拷贝

  • =(赋值) 浅拷贝:值相等,地址相等
  • copy浅拷贝:值相等,地址不相等
  • deepcopy深拷贝:值相等,地址不相等

当浅复制的值是可变对象(列表和元组)时会产生一个“不是那么独立的对象”存在。有两种情况:

  • 第一种情况:复制的 对象中无 复杂 子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。
>>> l1=[1,2,3]
>>> l2=l1
>>> l3=copy.copy(l1)
>>> id(l1)
4367654664
>>> id(l2)
4367654664
>>> id(l3)
4367628616
>>> l1.append(55)
>>> print(l1)
[1, 2, 3, 55]
>>> print(l3)
[1, 2, 3]
  • 第二种情况:复制的对象中有 复杂 子对象 (例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。 但是改变原来的值 中的复杂子对象的值 会影响浅复制的值。
>>> import copy
>>> list1=[1,2,['a','b']]
>>> list2=list1
>>> list3=copy.copy(list2)
>>> list4=copy.deepcopy(list3)
>>> id(list1)
4338414656
>>> id(list2)
4338414656
>>> id(list3)
4338415016
>>> id(list4)
4338414368
>>> list1[2].append('a')
>>> id(list1)
4338414656
>>> print list1
[1, 2, ['a', 'b', 'a']]
>>> print list3
[1, 2, ['a', 'b', 'a']]
>>> print list4
[1, 2, ['a', 'b']]

探讨:对于一个复杂对象的浅copy,在copy的时候到底发生了什么

>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin 里边有三个元素:1, 2,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False 
#cop1 和 cop2 看上去相同,但已不再是同一个object
>>> origin[2][0] = "hey!" 
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin内的子list [3, 4] 改掉了一个元素,观察 cop1 和 cop2
  • shallow copy 概念图如下:

image

shallow copy(浅复制)对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。

  • deepcopy 概念图如下:

image

deepcopy的时候会将复杂对象的每一层复制一个单独的个体出来。

这时候的 origin[2] 和 cop2[2] 虽然值都等于 [3, 4],但已经不是同一个 list了。即我们寻常意义上的复制。

参考文章: https://blog.csdn.net/u011630575/article/details/78604226#commentBox
https://blog.csdn.net/u014745194/article/details/70271868

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值