浅显直白的Python深拷贝与浅拷贝区别说明
一、可变数据类型与不可变数据类型
在开始说深拷贝与浅拷贝前,我们先来弄清楚,可变对象与不可变对象
总的来说,Python数据类型可分为可变数据类型与不可变数据类型
可变数据类型:在不改变对象所指向的地址的前提下,地址中的值是可以改变的,例如列表[1, 2, 3],我们可以改为[2,3]并不需要变更它指向的地址。列表、字典、集合都是可变数据类型
不可变数据类型:在不改变对象所指向的地址的前提下,地址中的值是不可变的,所以如果修改了对象的值,就相当于在另一个新的地址,存储了新的值。Python中元组、字符串、数值、布尔值都是不可变数据类型。
二、Python深拷贝与浅拷贝的区别
在弄清楚了可变对象和不可变对象之后,我们进入正题,看下Python的深拷贝与浅拷贝的区别
1. 浅拷贝:
仅拷贝父对象,可理解为仅拷贝对象第一层。浅拷贝之后,新旧对象本身指向的地址不同了,但子对象指向的地址仍然相同,我们可以用copy.copy()和可变数据类型的切片来进行浅拷贝
m = [1, 0, [2, 3, 4, [5, 6]]]
n = m.copy()
p = m[:]
print(f'对象m的地址是{id(m)},对象n的地址是{id(n)},对象p的地址是{id(p)}')
print(f'm[0]的地址是{id(m[0])},n[0]的地址是{id(n[0])},p[0]的地址是{id(p[0])}')
print(f'm[2]的地址是{id(m[2])},n[2]的地址是{id(n[2])},p[2]的地址是{id(p[2])}')
输出:
对象m的地址是1322908811144,对象n的地址是1322908811080,对象p的地址是1322908763400
m[0]的地址是140727539432512,n[0]的地址是140727539432512,p[0]的地址是140727539432512
m[2]的地址是1322908811208,n[2]的地址是1322908811208,p[2]的地址是1322908811208
打印结果可以看到,浅拷贝之后,新对象n, p的地址与m不同,但n, p的子对象地址与m中子对象地址是相同的
此时,我们对新对象的子对象进行修改,我们来修改一下n[0]看一下结果
n[0] =99
print(f' m[0]={m[0]}\n n[0]={n[0]}\n p[0]={p[0]}')
print(f' m[0]的地址:{id(m[0])}\n n[0]的地址:{id(n[0])}\n p[0]的地址:{id(p[0])}') 输出:
m[0]=1
n[0]=99
p[0]=1
m[0]的地址:140727543364672
n[0]的地址:140727543367808
p[0]的地址:140727543364672
可以看到,n[0]的地址和值都变了, m[0]和p[0]并没有变,是为什么呢?
记得咱们最开始介绍了可变对象和不可变对象,这里的n[0]是数值,是不可变对象,所以在地址不改变的情况下,它的值是不变的;
而我们在给它赋值时,相当于是把它指向了另一个地址,存储新值,而m[0]和p[0]指向的地址并没有变化
接下来咱们再来尝试变更一下n[2]吧
print('----------------------------修改前----------------------------------\n '
f'm[2]的地址:{id(m[2])}\n n[2]的地址:{id(n[2])}\n p[2]的地址:{id(p[2])}') n[2][1] = 'n21' print('----------------------------修改后----------------------------------\n'
f' m[2]={m[2]}\n n[2]={n[2]}\n p[2]={p[2]}')
print(f' m[2]的地址:{id(m[2])}\n n[2]的地址:{id(n[2])}\n p[2]的地址:{id(p[2])}') 输出:
----------------------------修改前----------------------------------
m[2]的地址:2235118923976
n[2]的地址:2235118923976
p[2]的地址:2235118923976
----------------------------修改后----------------------------------
m[2]=[2, 'n21', 4, [5, 6]]
n[2]=[2, 'n21', 4, [5, 6]]
p[2]=[2, 'n21', 4, [5, 6]]
m[2]的地址:2235118923976
n[2]的地址:2235118923976
p[2]的地址:2235118923976
可以看到,变更前后n[2]、m[2]和p[2]的地址都是没变的, 原因你已经知道了吧,是的,因为n[2]是可变对象,是可以在地址中直接变更值的。
2. 深拷贝
深拷贝完全父对象与子对象。可使用copy模块的deepcopy()方法进行深拷贝,此外使用for循环复制可迭代序列也是深拷贝
m = [1, 0, [2, 3, 4, [5, 6]]]
n1 = copy.deepcopy(m)
print(f'对象m的地址是{id(m)},对象n1的地址是{id(n1)}')
print(f'm[0]的地址是{id(m[0])},n1[0]的地址是{id(n1[0])}')
print(f'm[2]的地址是{id(m[2])},n1[2]的地址是{id(n1[2])}') 输出:
对象m的地址是2551218893640,对象n1的地址是2551219173192
m[0]的地址是140727516494912,n1[0]的地址是140727516494912
m[2]的地址是2551218833480,n1[2]的地址是2551221308040
打印结果可以看出,新旧对象本身的地址,和可变子对象地址都是不同的。
这里看到m[0]和n1[0]的地址相同,但不可变对象的值变更地址就会变更,所以不会有问题。
三、 总结
综上,咱们在实际应用中,如果拷贝对象的子对象都是不可变对象,那么使用浅拷贝和深拷贝都行,
但如果待拷贝对象中有可变子对象,需要注意根据实际需求选择使用深拷贝还是浅拷贝。
浅显直白的Python深拷贝与浅拷贝区别说明的更多相关文章
- Python深拷贝与浅拷贝区别
可变类型 如list.dict等类型,改变容器内的值,容器地址不变. 不可变类型 如元组.字符串,原则上不可改变值.如果要改变对象的值,是将对象指向的地址改变了 浅拷贝 对于可变对象来说,开辟新的内存 ...
- 【python测试开发栈】—理解python深拷贝与浅拷贝的区别
内存的浅拷贝和深拷贝是面试时经常被问到的问题,如果不能理解其本质原理,有可能会答非所问,给面试官留下不好的印象.另外,理解浅拷贝和深拷贝的原理,还可以帮助我们理解Python内存机制.这篇文章将会通过 ...
- Python 深拷贝和浅拷贝的区别
python的复制,深拷贝和浅拷贝的区别 在python中,对象赋值实际上是对象的引用.当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用 ...
- python深拷贝和浅拷贝的区别
首先深拷贝和浅拷贝都是对象的拷贝,都会生成一个看起来相同的对象,他们本质的区别是拷贝出来的对象的地址是否和原对象一样,也就是地址的复制还是值的复制的区别. 什么是可变对象,什么是不可变对象: 可变对象 ...
- Python深拷贝和浅拷贝
1- Python引用计数[1] 1.1 引用计数机制 引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象.内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的 ...
- python 深拷贝与浅拷贝
浅拷贝的方式有: lst=[1,2,3] (1)直接赋值: lst_cp = lst (2)for循环遍历生成:lst_cp= [i for i in lst] (3)copy模块下,copy.cop ...
- 【js】深拷贝和浅拷贝区别,以及实现深拷贝的方式
一.区别:简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,如果B没变,那就是深拷贝. 此篇文章中也会简单阐述到栈堆,基本数据类型与引用数据类型,因为这 ...
- Python——深拷贝和浅拷贝
深拷贝.浅拷贝 1. 浅拷贝 浅拷贝是对于一个对象的顶层拷贝 import copy a = [[1, 2], 3] b = copy.copy(a) print(id(a)) print(id(b) ...
- python中赋值,深拷贝,浅拷贝区别
这三种 的区别就是 复制的变量 是否是原变量的引用. 赋值:只是原变量的引用. 浅拷贝和深拷贝的区别 需要通过 子元素 区分 浅拷贝:子元素的 引用相同 深拷贝:所以引用都不相同,完全复制一份 这三种 ...
- python深拷贝与浅拷贝的区别
可变对象:一个对象在不改变其所指向的地址的前提下,可以修改其所指向的地址中的值 不可变对象:一个对象所指向的地址上值是不能修改的,如果你修改了这个对象的值,那么它指向的地址就改变了,相当于你把这个对象 ...
随机推荐
- windows使用pyinstaller 打包sklearn模块出现死循环报错
前言 解决这个让我花费了很长时间, 我这里说的死循环,不是正常通过 --hidden-import能解决的问题. 因为我也查询了很多资料 但是无一例外都失败了(能通过 –hidden-import 解 ...
- -webkit-box-orient:vertical 编译报错之autoprefixer问题
由于各大浏览器的兼容问题,autoprefixer 插件 就可以帮我们自动补齐前缀.它和 less.scss 这样的预处理器不同,它属于后置处理器. 预处理器:在打包之前进行处理 后置处理器:在代码打 ...
- nginx配置文件讲解及示例(可复制)
详细的配置说明参考:https://www.cnblogs.com/ghl1024/p/9013805.html [示例一] #运行用户user www-data; #启动进程,通常设置成和cpu ...
- 【SQL进阶】【REPLACE/TIMESTAMPDIFF/TRUNCATE】Day01:增删改操作
一.插入记录 1.插入多条记录 自己的答案: INSERT INTO exam_record(uid, exam_id, start_time, submit_time, score) VALUES ...
- EPSS 解读:与 CVSS 相比,孰美?
通用漏洞评分系统(CVSS)是当前应用最频繁的评分系统以评估安全漏洞的严重性.但是,由于该系统在评估漏洞和优先级排序方面存在不足而遭受批评.因此,有部分专业人士呼吁使用漏洞利用预测评分系统(EPSS) ...
- eval解析的函数传参 object array
1 const fn = (...args) => { 2 console.log(...args) 3 } 4 5 const handleEval = (fnName,...args) =& ...
- L1-049 天梯赛座位分配 (20分)
L1-049 天梯赛座位分配 (20分) 天梯赛每年有大量参赛队员,要保证同一所学校的所有队员都不能相邻,分配座位就成为一件比较麻烦的事情.为此我们制定如下策略:假设某赛场有 N 所学校参赛,第 i ...
- vulnhub靶场之DARKHOLE: 1
准备: 攻击机:虚拟机kali.本机win10. 靶机:DarkHole: 1,下载地址:https://download.vulnhub.com/darkhole/DarkHole.zip,下载后直 ...
- 【Java技术专题】「原理专题」深入分析Java中finalize方法的作用和底层原理
finalize方法是什么 finalize方法是Object的protected方法,Object的子类们可以覆盖该方法以实现资源清理工作,GC在首次回收对象之前调用该方法. finalize方法与 ...
- 【kubernetes入门到精通】Kubernetes的健康监测机制以及常见ExitCode问题分析「探索篇」
kubernetes进行Killed我们服务的问题背景 无论是在微服务体系还是云原生体系的开发迭代过程中,通常都会以Kubernetes进行容器化部署,但是这也往往带来了很多意外的场景和情况.例如,虽 ...