Python---copy()、deepcopy()与赋值的区别
copy()与deepcopy()之间的主要区别是python对数据的存储方式。
首先直接上结论:
—–深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。
—–而等于赋值,并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。
—–而浅复制要分两种情况进行讨论:
1)当浅复制的值是不可变对象(数值,字符串,元组)时和“等于赋值”的情况一样,对象的id值与浅复制原来的值相同。
2)当浅复制的值是可变对象(列表和元组)时会产生一个“不是那么独立的对象”存在。有两种情况:
第一种情况:复制的 对象中无 复杂 子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。
第二种情况:复制的对象中有 复杂 子对象 (例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。 但是改变原来的值 中的复杂子对象的值 会影响浅复制的值。
对于简单的 object,例如不可变对象(数值,字符串,元组),用 shallow copy 和 deep copy 没区别
复杂的 object, 如 list 中套着 list 的情况,shallow copy 中的 子list,并未从原 object 真的「独立」出来。也就是说,如果你改变原 object 的子 list 中的一个元素,你的 copy 就会跟着一起变。这跟我们直觉上对「复制」的理解不同。
当浅复制的值是不可变对象(数值,字符串,元组)时,代码如下:
>>> a="1234567"
>>> b=a
>>> id(a)
4367619440
>>> id(b)
4367619440
>>> c=copy.copy(a)
>>> id(c)
4367619440
>>> d=copy.deepcopy(a)
>>> id(d)
4367619440
当浅复制的值是可变对象(列表,字典)时,改变的值不是 复杂子对象 代码如下:
当浅复制的值是可变对象(列表,字典)时,改变的值是 复杂子对象 代码如下:
>>> 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']]
>>> list1.append(33)
>>> id(list1)
4338414656
>>> id(list3)
4338415016
>>> print list1
[1, 2, ['a', 'b', 'a'], 33]
>>> print list3
[1, 2, ['a', 'b', 'a']]
代码说明:当改变 复杂子对象中的元素时,浅复制值发生了变化; 当改变的值不是复杂子对象,浅复制的值没有发生变化。因为 浅复制 ,复杂子对象的保存方式是 作为 引用 方式存储的,所以修改 浅复制的值 和原来的值都可以 改变 复杂子对象的值。
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 变了
- 上面两段代码中,a 的值都发生了变化。区别在于,第一段代码中是直接赋给了 a 新的值(从 [1, 2, 3] 变为 [4, 5, 6]);而第二段则是把 list 中每个元素分别改变。而对 b 的影响则是不同的,一个没有让 b 的值发生改变,另一个变了。怎么用上边的道理来解释这个诡异的不同呢?首次把 [1, 2, 3] 看成一个物品。a = [1, 2, 3] 就相当于给这个物品上贴上 a 这个标签。而 b = a 就是给这个物品又贴上了一个 b 的标签。
第一种情况:a = [4, 5, 6] 就相当于把 a 标签从 [1 ,2, 3] 上撕下来,贴到了 [4, 5, 6] 上。在这个过程中,[1, 2, 3] 这个物品并没有消失。 b 自始至终都好好的贴在 [1, 2, 3] 上,既然这个 reference 也没有改变过。 b 的值自然不变。
第二种情况:a[0], a[1], a[2] = 4, 5, 6 则是直接改变了 [1, 2, 3] 这个物品本身。把它内部的每一部分都重新改装了一下。内部改装完毕后,[1, 2, 3] 本身变成了 [4, 5, 6]。而在此过程当中,a 和 b 都没有动,他们还贴在那个物品上。因此自然 a b 的值都变成了 [4, 5, 6]。
搞明白这个之后就要问了,对于一个复杂对象的浅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
学过docker的人应该对镜像这个概念不陌生,我们可以把镜像的概念套用在copy上面。
copy概念图如下:
copy(浅复制)对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。
所以说看这里的origin[2],也就是 [3, 4] 这个 list。根据 shallow copy 的定义,在 cop1[2] 指向的是同一个 list [3, 4]。那么,如果这里我们改变了这个 list,就会导致 origin 和 cop1 同时改变。这就是为什么上边 origin[2][0] = “hey!” 之后,cop1 也随之变成了 [1, 2, [‘hey!’, 4]]。
deepcopy概念图如下:
deepcopy的时候会将复杂对象的每一层复制一个单独的个体出来。
这时候的 origin[2] 和 cop2[2] 虽然值都等于 [3, 4],但已经不是同一个 list了。即我们寻常意义上的复制。
参考文章 http://iaman.actor/blog/2016/04/17/copy-in-python
Python---copy()、deepcopy()与赋值的区别的更多相关文章
- Python copy(), deepcopy()
copy() 浅拷贝: 创建一组拷贝对象的引用.切片操作相当于浅拷贝,会生成一个新的对象,新的对象里保存原对象的引用. 如果原对象中的可变对象改变(list),那么浅拷贝的对象随之改变,如果原对象中不 ...
- [Python] 等号赋值, copy, deepcopy的区别
参考链接: 1. 介绍python中的可变类型与不可变类型:https://blog.csdn.net/answer3lin/article/details/86430074 (也可以参考转载博客 P ...
- Python 函数参数引用(传值/传址)/copy/deepcopy
精简版: 传值:被调函数局部变量改变不会影响主调函数局部变量 传址:被调函数局部变量改变会影响主调函数局部变量 Python参数传递方式:传递对象引用(传值和传址的混合方式),如果是数字,字符串,元组 ...
- python copy和deepcopy
Python FAQ2:赋值.浅拷贝.深拷贝的区别? 发表于 2014-08-15 | 分类于 Lang.-Python | 在写Python过程中,经常会遇到对象的拷贝,如果不理解浅拷 ...
- Python copy and deepcopy
Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块. 1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象. 2. copy.deep ...
- Python copy对象(copy与deepcopy)
Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块. 1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象. 2. copy.deep ...
- Python 浅拷贝copy()与深拷贝copy.deepcopy()
首先我在这介绍两个新的小知识,要在下面用到.一个是函数 id() ,另一个是运算符 is.id() 函数就是返回对象的内存地址:is 是比较两个变量的对象引用是否指向同一个对象,在这里请不要和 == ...
- Python 传值和传址 copy/deepcopy
传值:被调函数局部变量改变不会影响主调函数局部变量 传址:被调函数局部变量改变会影响主调函数局部变量 Python参数传递方式:传递对象引用(传值和传址的混合方式),如果是数字,字符串,元组则传值:如 ...
- python copy与deepcopy (拷贝与深拷贝)
copy与deepcopy python 中的copy与deepcopy是内存数据的操作,但是两个函数有一定的区别. 1.copy import copy list = [1, [4, 5, 6], ...
随机推荐
- Python学习杂记_15_正则表达式
正则表达式 正则表达式就是用来查找字符串的,它能够查找规则比较复杂的字符串.使用正则表达式首先要导入re模块import re s = "besttest is good!besttest ...
- phpstudy无法访问主页,提示You don't have permission to access / on this server解决办法
1.输入localhost提示:You don't have permission to access / on this server. 新版phpStudy为了安全,取消Apache和nginx列 ...
- 牛客网 暑期ACM多校训练营(第二场)D.money-贪心 or 动态规划
D.money 贪心,直接贴官方的题解吧. 题目大意 你要按照顺序依次经过n个商店,每到达一个商店你可以购买一件商品,也可以出售你手中的商品. 同一时刻你手上最多拿一件商品.在第i个商店购买和出售的代 ...
- (1)安装Xamarin
()一.安装 1.安装xamarin 2.下载jdk8 https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads- ...
- CF978A Remove Duplicates【数组操作/STL】
[链接]:CF978A [分析]:逆向思考+标记数组去重 [代码]: #include<bits/stdc++.h> using namespace std; const int INF ...
- APP专项测试 | 内存及cpu
命令: adb shell dumpsys meminfo packagename 关注点: 1.Native/Dalvik 的 Heap 信息 具体在上面的第一行和第二行,它分别给出的是JNI层和 ...
- HDU 3237 Tree(树链剖分)(线段树区间取反,最大值)
Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 9123 Accepted: 2411 Description ...
- WSS3SDK之:服务器和站点架构:对象模型概览
源出处:http://www.cnblogs.com/Sunmoonfire/archive/2011/01/18/1937884.html Windows SharePoint Services提供 ...
- [xsy2213]tower
题意:给一个地图,地图上的每个位置是空地或一个炮或一些敌人,给定每个炮的方向(上/下/左/右),每个炮只能打一个位置且炮弹轨迹不能相交,问最多打到多少敌人 原题貌似是TC SRM 627的题,题解在这 ...
- linux命令和工具
环境搭建 lnmp环境搭建 命令 uname -a 查看linux版本 lsof -i:80 查看端口被那个程序占用 lsof -p pid号 查看引用的文件 netstat -apn|grep 80 ...