Python | 深浅拷贝(python深浅拷贝原理)
off999 2024-10-29 15:00 34 浏览 0 评论
同驻留机制一样,深浅拷贝在编程中的应用其实也不是很多。但是我们依然需要了解。一方面是为了避免后期开发代码时出现问题却不知道原因,另一方面也是因为面试时几乎都会问到有关深浅拷贝的问题。
深浅拷贝主要分为三个方面:
- 什么是赋值
- 什么是浅拷贝
- 什么是深拷贝
赋值
变量间赋值操作就是将多个变量的内存地址指向同一个数据的内存地址。例如,对于下面的代码:
a = 10
b = a
print(id(a), id(b))输出的结果为:
1947042192 1947042192a 和 b 的内存地址相同。这段赋值操作在内存中的程序是:首先,在内存中开辟一块内存空间储存整数 10,它的内存地址为 1947042192;然后,变量名 a 指向这个内存空间的内存地址;最后,变量名 b 同样指向这个内存地址。
对于整型数据这样的不可变数据类型来说,此时,如果重新给 a 进行赋值,比如赋值为 11。将会新开辟一块内存空间来存储整型数据 11,然后 a 将指向 11 的内存地址(比如:1947042224),原来的指向将不再存在。而 b 仍然指向 10 的内存地址 1947042192:
用代码来验证就是:
a = 10
b = a
a = 11
print(id(a), id(b))输出的结果为:
1947042224 1947042192对于可变数据类型的赋值操作则会有些例外。赋值操作的原则都是相同的,新变量名会指向已经存在的数据,而不是新开辟一块内存空间:
lst = [1, 2, 3]
lst1 = lst
print(id(lst), id(lst1))
for i in lst:
print(id(i))输出的结果为:
1334279969672 1334279969672
1947041904
1947041936
1947041968首先整型数字 1、2 和 3 分别开辟各自的内存空间。需要注意的是,列表中存储的是每个元素的内存地址,而不是元素本身。当整型数据内存空间开辟好了之后,又开辟了一块列表空间,其中存储的数据是三个整型数字的内存地址。然后,变量 lst 指向列表的内存地址 1334279969672。最后,赋值操作令变量 lst1 也指向该列表的内存地址 1334279969672。
这些操作看起来繁琐许多,但实际上的原理还是赋值操作只是将不同的变量指向同一个数据的内存地址。
但是现在,如果我们对 lst1 进行修改,比如增加一个新的元素 4,会发生什么呢?
lst = [1, 2, 3]
print(id(lst))
lst1 = lst
lst1.append(4)
print(lst, lst1)
print(id(lst), id(lst1))
for i in lst:
print(id(i))输出的结果为:
3009179196296
[1, 2, 3, 4] [1, 2, 3, 4]
3009179196296 3009179196296
1947041904
1947041936
1947041968
1947042000我们发现发生了一件奇怪的事情:虽然我们只对 lst1 进行了修改,但是 lst 也同样发生了改变。
这是因为列表是可变数据类型,当我们对列表进行操作时,不会改变列表的内存地址 ^1。使用 append 方法会在列表存储的内容中添加一个整型数据 4 的内存空间,但并不会改变变量 lst 和 lst1 指向列表内存的状态。故而,虽然只对 lst1 进行了修改,但是查看 lst 时,变化是相同的。
举一个形象一点的例子就是,列表就好比是一个书包。最开始,这个书包归小明所有。后来,小明跟好朋友小芳共用了这个书包。小芳有一天在书包里装了一本书,虽然小明没有对这个书包做任何的操作,他依然能够看到自己的书包里多出了一本书来。
对于字典等其他可变数据类型,道理都是相同的。对于可变数据类型的嵌套,也是一样:
dic = {'key1': 2, 'key2': [1, 2, 3]}
dic1 = dic
dic1['key2'].append(4)
print(dic, dic1)输出的内容为:
{'key1': 2, 'key2': [1, 2, 3, 4]} {'key1': 2, 'key2': [1, 2, 3, 4]}总结起来就是:赋值就是让多个变量指向同一个内存地址,如果这个内存地址的数据是不可变数据类型,修改时会开辟新的内存空间(字符串,数字,布尔值,元组);如果时可变的数据类型,会在原地址进行修改(列表,字典)。
浅拷贝
与赋值略有差异的是,浅拷贝会将数据的外壳新开辟一块内存空间,在新的内存空间中存储的是最外层元素的内存地址。
列表有一个 .copy() 方法,用来进行浅拷贝操作:
lst = [1, 2, [3, 4]]
lst1 = lst.copy() #拷贝 or 复制
print(lst, lst1)
print(id(lst), id(lst1))
print(id(lst[0]), id(lst1[0]))
print(id(lst[1]), id(lst1[1]))
print(id(lst[2]), id(lst1[2]))
print(id(lst[2][0]), id(lst1[2][0]))
print(id(lst[2][1]), id(lst1[2][1]))输出的结果为:
[1, 2, [3, 4]] [1, 2, [3, 4]]
2718491515720 2718491515912
1945534576 1945534576
1945534608 1945534608
2718491515784 2718491515784
1945534640 1945534640
1945534672 1945534672我们看到,除了最外层的壳子,也就是列表本身被新开辟了一块内存空间之外,无论列表中的元素是否可变,都只是复制了原来列表中元素的内存地址。
最开始,列表 lst 开辟了一块地址为 2718491515720 的内存空间,其中,数字 1 被存储在地址 1945534576,数字 2 被存储在地址 1945534608,列表被存储在地址 2718491515784。列表中的数字 3 被存储在地址 1945534640,列表中的数字 4 被存储在地址 1945534672。当进行浅拷贝之后,为 lst1 新开辟一块内存地址为 2718491515912 的内存空间,其中存储的元素的内存地址跟 lst 中的元素相同。
此时,如果我们对 lst1 进行增加操作,比如增加一个元素 5:
lst1.append(5)
print(lst, lst1)输出的结果为:
[1, 2, [3, 4]] [1, 2, [3, 4], 5]只有 lst1 中增加了元素,lst 并没有发生变化。这是因为 lst 和 lst1 的内存地址不同,对 lst1 进行操作并不会影响到 lst。
不过如果我们对列表中嵌套的列表进行增加操作,比如:
lst = [1, 2, [3, 4]]
lst1 = lst.copy()
lst1[-1].append(5)
print(lst, lst1)输出的结果为:
[1, 2, [3, 4, 5]] [1, 2, [3, 4, 5]]不管是 lst 还是 lst1,它们的最后一个元素都是指向的列表 [3, 4] 的内存地址 2718491515784。当对这个列表进行修改时,尽管是使用 lst 进行的操作,lst1 调用列表时,仍然能发现列表的变化。
除了使用 .copy() 方法,我们还可以通过切片来实现浅拷贝:
lst = [2, 3, [4, 5], 6]
lst1 = lst
lst2 = lst[:]
lst1[2].append(6)
print(lst, lst1, lst2)输出的结果为:
[2, 3, [4, 5, 6], 6] [2, 3, [4, 5, 6], 6] [2, 3, [4, 5, 6], 6]深拷贝
深拷贝之后的数据使用同我们主观认为的基本一致:对一个变量的修改不会影响到另一个变量的值。经过深拷贝后的两个变量虽然值相同,但是不受彼此影响。
当然,这也不意味着深拷贝后的两个变量中的每个元素都是不同的。为了节省内存,经过深拷贝后的不可变元素仍然被两个变量共用。因为对不可变元素的改变一定会造成内存地址的变化,所以不需要单独开辟内存空间。但是对于可变元素,不管嵌套多少层,都会开辟新的内存空间。
调用深拷贝方法需要导入一个 copy 模块:
import copy
lst = [1000, 2, [3, 4]]
lst1 = copy.deepcopy(lst)
print(id(lst), id(lst1))
print(id(lst[0]), id(lst1[0]))
print(id(lst[1]), id(lst1[1]))
print(id(lst[2]), id(lst1[2]))
print(id(lst[2][0]), id(lst1[2][0]))
print(id(lst[2][1]), id(lst1[2][1]))返回的结果为:
2277137687880 2277137689160
2277135326064 2277135326064
1945534608 1945534608
2277137687688 2277137689096
1945534640 1945534640
1945534672 1945534672与我们前面谈到的一样,深拷贝后,两个变量会共用不可变数据,而可变数据类型则会开辟新空间。
深浅拷贝总结
赋值:多个变量名指向同一个内存地址,也就是将变量和值在内存中形成映射指向关系
浅拷贝:只对最外层的壳子开辟内存空间,拷贝第一层元素的内存地址。(列表中存储的是数据的内存地址,我们能看到数据是因为程序通过内存地址找到值,然后显示了出来)
深拷贝:不可变数据共用,可变数据类型不管嵌套多少层都会开辟新空间
相关推荐
- 戴尔官网保修查询入口(戴尔售后保质期查询)
-
可以按照以下步骤查询戴尔笔记本电脑的保修期:1.打开戴尔官网:https://www.戴尔.com/zh-cn/售后服务/保修政策.html2.点击页面上方的“服务与支持”按钮,进入戴尔的服务支持...
- 手机号邮箱登录入口(手机号邮箱官网)
-
手机163邮箱登录入口如下:163邮箱官网入口:https://smart.mail.163.com/login.htm点击进入登录或者注册邮箱即可。手机浏览器访问进入官网http://www.123...
- sd卡(sd卡无法读取怎么修复)
-
SD卡是大卡,相机用的;普通的手机内存卡,是小卡,正规的名称是macrosd卡,也就是微型SD卡。可以通过卡套转为普通的SD卡的大小。 其实就是大小不同。但手机上的内存卡,人们经常也俗称为SD...
- windows7蓝牙功能在哪里打开
-
点击搜索框在windows7系统主界面点击开始菜单,点击打开搜索框。输入命令输入services.msc后回车,在列表中找到并右击BluetoothSupportS...点击属性选择进入属性菜单,...
-
- 2010激活密钥(microsoft2010激活密钥)
-
步骤/方式1officeprofessionalplus2010:(office专业版)6QFdx-pYH2G-ppYFd-C7RJM-BBKQ8Bdd3G-xM7FB-Bd2HM-YK63V-VQFdKVYBBJ-TRJpB-QFQ...
-
2025-11-19 04:03 off999
- 联想官方刷新bios工具(联想电脑刷新bios)
-
刷新BIOS需要使用联想的官方网站或授权维修中心来进行操作。以下是一些基本步骤:1.访问联想的官方网站,找到BIOS更新程序并下载。在下载过程中,请确保选择与您计算机型号匹配的版本。2.将下载的B...
-
- 苹果ios14系统下载(苹果ios14.1下载)
-
1方法一步骤/方式一打开Appstore。步骤/方式二在搜索栏点击搜索框。步骤/方式三搜索并点击需要下载的软件。步骤/方式四点击获取。步骤/方式五最后验证ID密码即可。1.在应用商店搜索你要下载的应用名称。2.点击下载按钮,如果要求登...
-
2025-11-19 03:03 off999
- office2010怎么免费永久激活密钥
-
用这个试试,一个KMS激活工具可以激活2010到2019的Office自家的目前用的就是这个microsoft6477.moe/1716.html直接使用这个Microsoftoffice2010...
-
- 类似爱加速的国内ip(类似爱加速的app)
-
推荐“V8盒子”。这一款免费无广告的模拟器,不同于其它软件盒子,而是类似于X8沙箱,满足游戏多开,画中画,悬浮球操作,熄屏后台运行等多功能的沙箱盒子.支持一键root,一键安装xposed框架,能在安卓/苹果手机上运行多个安卓/ios虚拟系...
-
2025-11-19 02:03 off999
- 阿里旺旺手机客户端(阿里旺旺手机app)
-
手机淘宝的旺旺在打开商品后,会看到左下角有个旺旺的图标,点击就可以联系了。 阿里旺旺是将原先的淘宝旺旺与阿里巴巴贸易通整合在一起的一个新品牌。它是淘宝和阿里巴巴为商人量身定做的免费网上商务沟通软件,...
- 最纯净的pe装机工具(pe工具哪个纯净)
-
U盘装系统步骤:1.制作U盘启动盘。这里推荐大白菜U盘启动盘制作工具,在网上一搜便是。2.U盘启动盘做好了,我们还需要一个GHOST文件,可以从网上下载一个ghost版的XP/WIN7/WIN8系统,...
- 装一个erp系统多少钱(wms仓库管理软件)
-
现在主流有客户端ERP和云端ERP两种客户端通常一次买断,价格在万元左右,但是还有隐性费用,你需要支付服务器、数据管理员,此外如果系统需要更新维护,你还需要支付另外一笔不菲的费用。云端ERP:优势...
- cad2014序列号和密钥永久(autocad2014序列号和密钥)
-
1在cad2014中修改标注样式后,需要将其保存2单击“样式管理器”按钮,在弹出的窗口中选择修改后的标注样式,然后单击“设置为当前”按钮,再单击“保存当前样式”按钮,将其保存为新的样式名称3为了...
- qq修改密保手机号(qq修改密保手机号是什么意思)
-
QQ更改绑定的手机号码操作步骤如下:1、打开手机主界面,找到“QQ”软件点击打开。2、输入正确的QQ账户和密码登录到qq主界面。3、点击左上角的头像“图片”,进入到个人中心界面。4、进入到个人中心界面...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
