网淘巴来吧,欢迎您!

返回首页 微信
微信
手机版
手机版

Python深拷贝和浅拷贝解析汇总

2021-05-16 新闻来源:网淘巴 围观:376
''
这篇文章主要介绍了Python中的深拷贝和浅拷贝详解,本文讲解了变量-对象-引用、可变对象-不可变对象、拷贝等内容。
 
浅拷贝:copy.copy() 只拷贝最外层的数据,如list1 = [a, b] 其中 a = [1, 2],b = [3, 4],执行 list2 = copy.copy(list1) 时,list2 中只会拷贝了 a, b 的指向,不会在内存区域中创建一份完全一样的数据
深拷贝:copy.deepcopy() 拷贝所有的数据,存入一块新的内存区域中。

特殊的情形:

用copy.copy()和copy.deepcopy(),即深拷贝和浅拷贝,对一个内部全部是不可变类型的数据(如元组中嵌套元组等)进行拷贝,那么他们的结果相同,都是拷贝指向;
如果对一个拥有可变类型数据的元组进行拷贝,那么copy.deepcopy()就是依然是拷贝所有数据, copy.copy()拷贝的是指向。

要说清楚Python中的深浅拷贝,需要搞清楚下面一系列概念:
变量-引用-对象(可变对象,不可变对象)-切片-拷贝(浅拷贝,深拷贝)
 
【变量-对象-引用】
在Python中一切都是对象,比如说:3, 3.14, 'Hello', [1,2,3,4],{'a':1}......

甚至连type[文]其本身都是对象[章],type对象[来]

Python中变量与C/C++/Java中不同,它是指对象的引用,Python是动态类型,程序运行时候,会根据对象的类型
来确认变量到底是什么类型。

单独赋值: 比如说:

代码如下:

>>> a = 3

在运行a=3后,变量a变成了对象3的一个引用。在内部,变量事实上是到对象内存空间的一个指针

因为Python的变量不过是对象的引用,或指向对象的指针,因此在程序中可以经常改变变量引用

 代码如下:

>>> x = 42      #变量绑定到整型对象
>>> x = 'Hello' #现在又成了字符串
>>> x = [1,2,3] #现在又成了列表

专业表述如下:

变量是一个系统表的元素,拥有指向对象的连接的空间
对象是被分配的一块内存,存储其所代表的值
引用是自动形成的从变量到对象的指针
特别注意: 类型属于对象,不是变量


比如像刚才的a=3, 整数对象3包含了两重信息
1.值为3
2.一个头部信息:告诉Pthyon,这是个整数对象[相当于一个指向int的指针]

共享引用: 比如说:

 代码如下:

>>> a = 3
>>> b = a

在运行赋值语句b = a之后,变量a和变量b指向了同一个对象的内存空间.

 

从上图可以看到,a和b,其id完全一样,指向同一个整数对象3,或者说同一块内存,如果删掉a后, 不会影响b。

拷贝概念的引入[自]就是针对:可变[网]对象的共享引用[淘]潜在的副作用而[巴]提出的.


【可变对象-不可变对象】
在Python中不可变对象指:一旦创建就不可修改的对象,包括字符串,元祖,数字
在Python中可变对象是指:可以修改的对象,包括:列表、字典
上面说的a,b都是整数,整数是不可变对象,如果是可变对象的话,就是另外一回事了。

代码如下:

>>> L1 = [2,3,4]      #L1变量指向的是一个可变对象:列表 
>>> L2 = L1           #将L1值赋给L2后,两者共享引用同一个列表对象[1,2,3,4] 
>>> L1[0] = 200       #因为列表可变,改变L1中第一个元素的值 
>>> L1; L2            #改变后,L1,L2同时改变,因为对象本身值变了 
[200, 3, 4] 
[200, 3, 4] 

如果不想改变列表L2的值,有两种方法:切片 和 copy模块
复制代码 代码如下:

>>> L1 = [2,3,4]  
>>> L2 = L1 
>>> id(L1);id(L2)     #共享引用一个可变对象 
45811784L 
45811784L 
>>> L2 = L1[:]        #切片操作 
>>> id(L1);id(L2)     #切片后,对象就不一样了 
45811784L 
45806920L 
>>> L1[0] = 200 
>>> L1;L2             #L1发生改变,L2没有变化 
[200, 3, 4] 
[2,   3, 4] 

 

【拷贝】
1. 切片技术应用于所有的序列,包括:列表、字符串、元祖
   >>>但切片不能应用于字典。对字典只能使用D.copy()方法或D.deepcopy()方法.

2. 深浅拷贝,即可用于序列,也可用于字典

代码如下:
   >>> import copy
   >>> X = copy.copy(Y)      #浅拷贝:只拷贝顶级的对象,或者说:父级对象
   >>> X = copy.deepcopy(Y)  #深拷贝:拷贝所有对象,顶级对象及其嵌套对象。或者说:父级对象及其子对象

浅拷贝和深拷贝

先看一下官方文档的定义

The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or
class instances).
A shallow copy constructs a new compound object and then (to the
extent possible) inserts the same objects into it that the
original contains.
A deep copy constructs a new compound object and then, recursively,inserts copies into it of the objects found in the original.

从文档中不难看出,上面提到深拷贝和浅拷贝两者区别在于在复合对象,那接下来也只讨论复合对象.

浅拷贝

 

注意到官方文档[文]也提到对浅拷贝[章]和深拷贝的定义[来],从上文中不难[自]看出,浅拷贝构[网]建一个复合对象[淘],然后将原有复[巴]合对象包含的对[文]象插入到新的复[章]合对象中

从上图不难看出[来],浅拷贝后,新[自]复合对象包含的[网]对象(可变或者[淘]不可变)的 id 值和原有对象包[巴]含的对象的 id 值相同

看一下具体例子:

1
2
3
4
5
6
7
8
9
>>> import copy
>>> a=[1,2,[3,4]]
>>> b=copy.copy(a)
>>> id(b[0])==id(a[0])
True
>>> id(b[2])==id(a[2])
True
>>> id(b[2][0])==id(a[2][0])
True

现在让我们试着修改一下浅拷贝后的 b 的值,在修改前,可以先思考一下,如果修改 b[0] 可能会发生什么?

由于 b[0] = 1,很显然 1 属于不可变对象,那么根据对不可变变量修改的规则,则 b[0] 会绑定到新的变量上,而 a[0] 的由于没有修改,则保持不变,真的是这样吗?让我们验证一下

1
2
3
4
5
>>> b[0]=5
>>> b
[5, 2, [3, 4]]
>>> a
[1, 2, [3, 4]]

接下来我们要尝试修改一下 b[2],由于 b[2] 绑定的对象是 list,属于可变对象,按照上面说的可变对象修改的规则,则修改后的 b[2] 的 id 值保持不变,但是其 value 值会发生改变. 同样的让我们通过例子验证一下

1
2
3
4
5
6
7
8
9
>>> id(b[2])
4300618568
>>> b[2][0]=6
>>> id(b[2])
4300618568
>>> b
[5, 2, [6, 4]]
>>> a
[1, 2, [6, 4]]

由于 b[2] 和 a[2] 绑定同一个可变对象,很显然对 b[2] 的修改同样会映射到 a[2] 

深拷贝

深拷贝构建一个[文]复合对象,然后[章]递归的将原有复[来]合包含的对象的[自]副本插入到新的[网]复合对象中

若上图所示,深[淘]拷贝后,新的复[巴]合对象包含的对[文]象,若对象为不[章]可变对象,则 id 值保持不变,若[来]对象为可变对象[自],则 id 值发生改变

看一个例子:

1
2
3
4
5
6
7
8
9
>>> import copy
>>> a=[1,2,[3,4]]
>>> b=copy.deepcopy(a)
>>> id(b[0])==id(a[0])
True
>>> id(b[2])==id(a[0])
False
>>> id(b[2][0])==id(a[2][0])
True

接下来让我们修改一下变量 b,这里就不在修改不可变对象 b[0] 和 b[1] 了,因为结果很明显,对 a 不会产生任何影响,我们来修改 b[2],那么修改 b[2] 会对 a[2] 产生影响吗?很明显答案是不会,因为深拷贝就相当于克隆出了一个全新的个体,两者不再有任何关系

1
2
3
4
5
>>> b[2][0]=5
>>> b
[1, 2, [5, 4]]
>>> a
[1, 2, [3, 4]]

以上就是详解Python直接赋值,深拷贝和浅拷贝的详细内容,更多关于Python直接赋值,深拷贝和浅拷贝的资料请关注脚本之家其它相关文章!


【结论】

深浅拷贝都是对源对象的复制,占用不同的内存空间
如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
序列对象的切片其实是浅拷贝,即只拷贝顶级的对象
 
免责申明
一、网淘巴提供的资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱:jjq_hn@wtao8.com ,我们会在看到邮件的第一时间内为您处理。二、网友通过wtao8.com途径发表的一切信息,本站有权在本网站范围内引用、发布、转载用户在本站发布的内容。本站对于用户发布的内容所引发的版权、署名权的异议、纠纷不承担任何责任。传统媒体转载须事先与原作者和本站联系。提交者发言纯属个人行为,与本网站立场无关。
打赏

相关文章

关于已赞助未发注册码声明

目前有一位朋友已赞助了,但未将软件软件名称、注册名和注册码发到我的邮箱,请将信息发至我的邮箱,谢谢。