深度解析:python之浅拷贝与深拷贝
深度解析python之浅拷贝与深拷贝
本文包括知识点:
1.copy与deepcopy
2.可变类型与不可变类型
1.copy与deepcopy
在日常python编码过程中,经常会遇见变量的赋值。这一部分会用代码+图解的形式解释=,copy,deepcopy的区别。
1. 直接赋值
Bill = ["Gates", 50, ["Python", "C#", "JavaScript"]]
Jack = Bill
print id(Bill)
print Bill
print [id(ele) for ele in Bill]
print id(Jack)
print Jack
print [id(ele) for ele in Jack]
print Jack is Bill
#output:
97597256
['Gates', 50, ['Python', 'C#', 'JavaScript']]
[97625072L, 34047944L, 97620808L]
97597256
['Gates', 50, ['Python', 'C#', 'JavaScript']]
[97625072L, 34047944L, 97620808L]
True
Bill[0] = "Mark"
Bill[2].append("Scala")
print id(Bill)
print Bill
print [id(ele) for ele in Bill]
print id(Jack)
print Jack
print [id(ele) for ele in Jack]
#output:
97262024
['Mark', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
[97615224L, 34047944L, 97596616L]
97262024
['Mark', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
[97615224L, 34047944L, 97596616L]

结论:
这说明采用直接赋值的方式,变量Bill和Jack的地址完全相同,同时他们的内部元素的地址也完全相同,Bill的元素修改Jack上也成立。需要注意的是,str与int等原子类型是不可变类型,在不改变地址的前提下,无法改变值,所以Bill[0]的值发生了变换,且分配了一个新的地址。而list ,dict等容器类型式可变类型,在地址不变的前提下,可以更改值,所以Bill[2]的值发生了变化,但是地址不变。
2. 浅拷贝
再来看下面一段代码:
import copy
Bill = ["Gates", 50, ["Python", "C#", "JavaScript"]]
Jack = copy.copy(Bill)
print id(Bill)
print Bill
print [id(ele) for ele in Bill]
print id(Jack)
print Jack
print [id(ele) for ele in Jack]
print Jack is Bill
#output:
97263496
['Gates', 50, ['Python', 'C#', 'JavaScript']]
[97625072L, 34047944L, 97262024L]
98156168
['Gates', 50, ['Python', 'C#', 'JavaScript']]
[97625072L, 34047944L, 97262024L]
False
Bill[0] = "Mark"
Bill[2].append("Scala")
print id(Bill)
print Bill
print [id(ele) for ele in Bill]
print id(Jack)
print Jack
print [id(ele) for ele in Jack]
#output:
97263496
['Mark', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
[97613904L, 34047944L, 97262024L]
98156168
['Gates', 50, ['Python', 'C#', 'JavaScript', 'Scala']]
[97625072L, 34047944L, 97262024L]

这说明,浅拷贝的变量Jack与Bill的地址不同,即 Jack is not Bill, 但是它俩的元素地址相同,即Jack[i] is Bill[i]。
浅拷贝之后,对原变量元素进行赋值。Bill[0]与Bill[-1]都发生了变化,且由于Bill[0]是不可变类型,分配了新的地址,而浅拷贝对象Jack[0]还指向原地址,所以Jack[0]的值还是原来的值。而Bill[-1]是原地变化,所以Bill[-1]也进行了相应的变化。
3.深拷贝
老惯例,先上代码:
import copy
Bill = ["Gates", 50, ["Python", "C#", "JavaScript"]]
Jack = copy.deepcopy(Bill)
print id(Bill)
print Bill
print [id(ele) for ele in Bill]
print id(Jack)
print Jack
print [id(ele) for ele in Jack]
print Jack is Bill
#output:
97262024
['Gates', 50, ['Python', 'C#', 'JavaScript']]
[97625072L, 34047944L, 97598216L]
97263496
['Gates', 50, ['Python', 'C#', 'JavaScript']]
[97625072L, 34047944L, 97596872L]
False
Bill[1] = 38
Bill[2].append("Scala")
print id(Bill)
print Bill
print [id(ele) for ele in Bill]
print id(Jack)
print Jack
print [id(ele) for ele in Jack]
#output:
97262024
['Gates', 38, ['Python', 'C#', 'JavaScript', 'Scala']]
[97625072L, 34048232L, 97598216L]
97263496
['Gates', 50, ['Python', 'C#', 'JavaScript']]
[97625072L, 34047944L, 97596872L]

结论,可以看到,Jack是Bill的深拷贝对象,Jack is not Bill,and Jack[i] is not Bill[i],从而实现效果上,两个变量的彻底剥离。
在初始化的时候,由于前两个元素都是不可变类型,Jack与Bill的这两个元素的地址相同。重点是对于可变类型元素,两个变量的地址是不同的,因此,不论元素类型是否可变,原对象的变化都不影响拷贝对象。
4.总结
= ,copy,deepcopy的区别如下:
考虑对象A的变化对拷贝对象B的影响,应该这么考虑:
1.A与B是否指向同一个地址(=),若是则A的任何变化都会导致B的变化。
2.A与B指向的地址不同,若B的元素与A的元素指向地址相同(浅拷贝),且A的元素有可变类型,那么A的可变类型元素的变化会导致B的相应元素的变化。
3.A与B指向的地址不同,且A与B的元素指向的地址也不同(深拷贝,不可变类型元素地址相同,不影响),那么A的任何类型元素的变化都不会导致B的元素变化。
(引申知识点):
2.可变类型与不可变类型
什么是可变/不可变? 简言之,就是内存地址(id)和type不变的前提下,value是否是可变的。
int ,float , str,tuple是不可变的,dict,list,set等容器类型是可变的。
不可变类型的变量若重新赋值,就是在新的地址上创建一个新的值,并把该变量指向新的地址,若之前的变量没有其他引用的话,就直接回收旧地址。可变类型的变量可以通过append,pop,remove等操作变换值,但是地址不会变更。
需要注意的是,tuple虽然是一种容器,但由于在初始化之后不能再更改,所以也是不可变类型。
引申:collections包中的其他容器类型,例如OrderedDict,Iterator,Container等是可变还是不可变呢? 读者不妨自己做做实验和总结。
深度解析:python之浅拷贝与深拷贝的更多相关文章
- python的浅拷贝和深拷贝
python对象有两种拷贝的形式:浅拷贝和深拷贝. 在<python核心编程>中看到对这两种拷贝的分析,觉得十分收益,所以记录在此. id()方法:id()方法可以查看某个对象的ID,类似 ...
- python:浅拷贝与深拷贝
1,“相等”与“相同” 我们先赋值三个变量a, b, c: a = [1, 2, [1, 2]] b = [1, 2, [1, 2]] c = a 判断一下‘相等’: a == b 返回 True ...
- 浅析JavaScript解析赋值、浅拷贝和深拷贝的区别
文章首发于sau交流学习社区 一.赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为: 1.基本数据类型:赋值,赋值之后两个变量互不影响 2.引用数据类型:赋**址**,两个变量具有相同 ...
- python之浅拷贝和深拷贝
1.浅拷贝 1>赋值:从下面的例子我们可以看到赋值之后新变量的内存地址并没有发生任何变化,实际上python中的赋值操作不会开辟新的内存空间,它只是复制了新对象的引用,也就是说除了b这个名字以外 ...
- Python的浅拷贝与深拷贝
定义: =号浅拷贝:在Python中对象的赋值其实就是对象的引用.copy了之后两个仍然是同一个东西.那么他们内部的元素自然也是一样的,对其中一个进行修改,另一个也会跟着变> copy()浅拷贝 ...
- Python 列表浅拷贝与深拷贝
浅拷贝 shallow copy 和深拷贝 deep copy list.copy() 浅拷贝:复制此列表(只复制一层,不会复制深层对象) 等同于 L[:] 举例: 浅拷贝: a = [1.1, 2. ...
- Python中浅拷贝和深拷贝的区别总结与理解
单层浅拷贝 import copy a = 1 # 不可变数据类型 copy_a = copy.copy(a) print(id(a),id(copy_a)) # 内存地址相同 a = [1,2] # ...
- python中浅拷贝和深拷贝分析
首先,我们知道Python3中,有6个标准的数据类型,他们又分为可以变和不可变.不可变:Number(数字).String(字符串).Tuple(元组).可以变:List(列表).Dictionary ...
- python中浅拷贝和深拷贝的区别
浅拷贝 可变类型浅拷贝copy函数就是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象可变类型:a = [1, 2, 3] b = [11, 2 ...
随机推荐
- c++11 语言级线程
c++11 语言级线程 线程的创建 用std::thread创建线程非常简单,只需要提供线程函数或函数对象即可,并且可以同时指定线程函数的参数. #define _CRT_SECURE_NO_WARN ...
- 【刷题】洛谷 P4234 最小差值生成树
题目描述 给定一个标号为从 \(1\) 到 \(n\) 的.有 \(m\) 条边的无向图,求边权最大值与最小值的差值最小的生成树. 输入输出格式 输入格式: 第一行两个数 \(n, m\) ,表示图的 ...
- 解题:HEOI 2013 SAO
题面 不好讲,直接上式子吧=.= 设$dp[i][j]$表示考虑完$i$的子树后$i$的排名为$j$的方案数,然后转移类似树形背包,具体来说是(这里假设子树在$i$后选,其实反过来还用这个式子答案也是 ...
- [POI2011]ROT-Tree Rotations
发现x的子树在后续处理中不会影响逆序对的情况(只关心有哪些值,相对位置已经不重要了) f[x]表示x为根的子树最小逆序对数 考虑左右儿子交换与否. 暴力是O(n^2)的 考虑线段树合并 左右儿子线段树 ...
- python之旅:字符编码
一 了解字符编码的知识储备 一 计算机基础知识 知识储备:cpu.内存.硬盘 二 文本编辑器存取文件的原理(nodepad++,pycharm,word) #1.打开编辑器就打开了启动了一个进程,是在 ...
- POI的XWPFParagraph.getRuns分段问题 多余逗号
POI的XWPFParagraph.getRuns分段问题 2018年08月28日 09:49:32 银爪地海贼 阅读数:376 这是我的模板 后台打印出来是分段的 造成这样的原因是${name} ...
- config配置中心之自动刷新
自动刷新(自动刷新是基于springcloudbus来实现的,springcloud bus是基于rabbitMQ或者Kafka来实现的) Spring Cloud Bus 将分布式的节点用轻量的消息 ...
- Chapter1(预科)--C++Prime笔记
心得体会: 因为之前一直在用在学C,因此在看完C++Prime第一章后,就有中在一个培训班中,一个老师用一个简单的项目来带你了解这种语言的特性的感觉.当然这个告诉是在让你脑子固化接受一些点的前提下. ...
- 在VC6/VC2005下使程序直接具有XP风格(XP Style):
原文 首先将以下文本保存为XpStyle.mainfest(后经实践,文件名和后缀是什么都无所谓) <?xml version="1.0" encoding="UT ...
- python基础-异常(exception)处理
python基础-异常(exception)处理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 程序中难免出现错误,而错误分成两种,即语法错误和逻辑错误.语法错误根本过不了pyth ...