当前位置: 首页 > news >正文

python之深浅copy

python之深浅copy

文章目录

  • python之深浅copy
    • 引入
      • 1.为什么要使用深浅拷贝
    • 一.赋值运算
      • 1.赋值运算原理
      • 2.示例
      • 3.总结
    • 二、拷贝
    • 三.浅拷贝
        • 什么是浅拷贝?
      • 1.浅拷贝原理
        • 创建浅拷贝的几种方式:
      • 2.示例
      • 3.总结
    • 四.深拷贝
        • 什么是深拷贝?
      • 1.深拷贝原理
        • 创建深拷贝:
      • 2.示例
      • 3.总结

引入

1.为什么要使用深浅拷贝

  • 涉及到容器类型的修改操作时,想要保留原来的数据或修改后的数据,这是就需要使用到深浅拷贝来进行操作了

  • 以下使用列表list1 = [ "str", 123, [111,222]]拷贝来进行实验

一.赋值运算

1.赋值运算原理

  • 赋值操作是指源列表与新列表指向的是同一个内存地址,无论是通过新变量名还是原变量名改变变量值内的元素,二者所对应的变量值都会改变(因为是同一个)。
  • list2 = list1

赋值操作

2.示例

?创建一个列表,包含字符串,整形和列表(可变),并将其赋值给另一个变量名
list1 = ["str", 123, [111,222]]
list2 = list1

?查看两个列表的"id",可以发现"id"不变,是同一个
print(id(list1))  #2248006456392
print(id(list2))  #2248006456392
print(id(list1[0]),id(list1[1]),id(list1[2]))
# 2248006252720 140725163491392 2248006455880
print(id(list2[0]),id(list2[1]),id(list2[2]))
# 2248006252720 140725163491392 2248006455880

?当改变"list1"内的第一或者第二个元素(不可变元素)
list1[0] = "aaa"
print(list1[0],id(list1[0]))
# aaa 2248007720496
print(list2[0],id(list2[0]))
# aaa 2248007720496
?发现"list1"改变的值的内存地址已经变化了,说明是产生的新值,与原来的"str"取消了对应
?而"list2"中的值和"id"也跟着改变

?当改变子列表里面的值时
list1[2][0] = 333
print(list1[2],id(list1[2]))
# [333, 222] 2248006455880
print(list2[2],id(list2[2]))
# [333, 222] 2248006455880
?我们发现两个列表的值都发生了改变,但子列表的"id"还都是不变的

3.总结

  • 赋值操作列表与新列表都是指向同一内存地址,所以他们都完全一样。
  • 2个列表中,只要有一个人的列表中的索引所对应的值的内存地址改变,则都改变
  • 也就是把源列表容器的内存地址完完整整的多绑定一份交给新列表

二、拷贝

拷贝是音译的词,是从copy这个英文单词音译过来的,copy其实就是复制一份。

在Python中,列表的内存地址存放的是元素的索引与元素内存地址的对应关系。

三.浅拷贝

什么是浅拷贝?

浅拷贝指仅拷贝对象的第一层,是在内存中重新开辟了一个空间存放索引与元素内存地址的对应关系。

对于原列表内的不可变数据类型,浅拷贝指向的内存地址和原对象指向的内存地址是同一个。当原对象第一层的元素改变,指向一个新的内存地址,而浅拷贝的内存地址指向的还是原内存地址。当原对象的深层元素改变,指向一个新的内存地址,浅拷贝的内存地址指向也会跟着变。

对于列表内的可变数据类型,浅拷贝指向的内存地址和原对象指向的内存地址是也同一个。但对**子列表内的元素,并没有指向列表内元素的内存地址。当子列表内的元素改变,子列表的内存地址并没有变,子列表内的元素内存地址改变。**浅拷贝指向子列表的内存地址不变,原子列表内存元素地址改变,浅拷贝指向的子列表元素内存地址也一起改变。

结论:当列表内第一层不可变数据类型的元素改变,浅拷贝的元素不会改变(还是指向原内存地址)。当列表内可变数据类型的子元素改变,浅拷贝的可变数据类型内的子元素也会跟着一起改变(仅指向可变数据类型本身)。

img

image-20201124091611162

1.浅拷贝原理

  • 把源列表第一层的内存地址不加区分(不区分是可变还是不可变类型)的完全copy给一份新列表
  • 对源列表copy之后, 产生的新列表的内存地址发生了改变, 它们不再是同一个列表
  • 但是新列表与源列表中的可变和不可变类型的值在修改之前都是指向同一个值
  • list2 = list1.copy()

浅拷贝

创建浅拷贝的几种方式:

  • 列表内置方法
l1 = [1, 'b', 'c', [10, 20, 30]]
l2 = copy.copy((l1))
  • 完全切片
l1 = [1, 'b', 'c', [10, 20, 30]]
l2 = l1[:]
  • copy模块
import copy
l1 = [1, 'b', 'c', [10, 20, 30]]
l2 = copy.copy(l1)

2.示例

?创建一个列表,包含字符串,整形和列表(可变),并将其浅拷贝
list1 = ["str", 123, [111,222]]
list2 = list1.copy()

?查看两个列表的"id",可见是产生了新的一个内存地址
print(id(list1))  #2243511014472
print(id(list2))  #2243541368584
print(id(list1[0]),id(list1[1]),id(list1[2]))
# 2243510810800 140725163491392 2243511013960
print(id(list2[0]),id(list2[1]),id(list2[2]))
# 2243510810800 140725163491392 2243511013960
?复制过来的元素内存地址都是同一个,说明复制的只是变量名与内存地址的对应关系,指向的是同一个内存地址

?当改变"list1"内的第一或者第二个元素(不可变元素)
list1[0] = "aaa"
print(list1[0],id(list1[0]))
# aaa 2240322768432
print(list2[0],id(list2[0]))
# str 2243510810800
?发现"list1"改变的值的内存地址已经变化了,说明是产生的新值,与原来的"str"取消了对应
?而"list2"中的值和"id"都没变,说明还是原来的对应关系

?当改变子列表里面的值时
list1[2][0] = 333
print(list1[2],id(list1[2]))
# [333, 222] 2243511013960
print(list2[2],id(list2[2]))
# [333, 222] 2243511013960
?我们发现两个列表的值都发生了改变,但子列表的"id"还都是不变的

3.总结

  • 对源列表中不可变类型的值进行修改以后,对于不可变类型的值,都是产生新值,让源列表的索引指向新的内存地址,并不会影响新列表
  • 对源列表中可变类型的值进行修改以后,对于可变类型,我们可以改变类型中包含的值,但这个可变容器本身内存地址不变
  • 即新列表的索引仍然指向原来的内存地址,于是新列表也跟着受影响。
  • 对于浅copy来说,只是在内存中重新创建了开辟了一个空间存放一个新列表,但是新列表中的元素与原列表中的元素是公用的。
  • 浅copy:列表、字典嵌套的数据类型是同一个。

四.深拷贝

什么是深拷贝?

深拷贝指拷贝对象的每一层,每一层都是在内存中重新开辟了一个空间存放一个新列表。

对于原列表内的每一层不可变数据类型,包括原列表内的子列表内的不可变数据类型,深拷贝指向的内存地址和原列表指向的内存地址是同一个。当原对象的值改变,会指向一个新的内存地址。而深拷贝的内存地址指向的还是原内存地址。

对于列表内的可变数据类型,深拷贝会重新申请一块内存空间,存放原列表内元素索引与元素id的对应关系。

结论:当列表内每一层不可变数据类型的元素改变,深拷贝内的元素都不会改变(指向的是原内存地址)。当列表内可变数据类型(列表本身,而非列表内的不可变类型元素)改变,深拷贝还是不变(可变类型深拷贝是重新申请了内存空间)。

未命名文件(7)

1.深拷贝原理

  • 深拷贝对容器类型中的每一层得数据加以区分 (对可变不可变类型区分对待)
  • 针对不可变类型, 拷贝以后任然还是用原来值的内存地址, 但值一发生改变就会产生新值, 于是与原来的值没有任何关联
  • 针对可变类型, 拷贝之后会申请新的内存地址存放, 在新的内存地址中再次区分(可变还是不可变)
  • 再次区分判断后, 于是重复针对不可变类型和针对可变类型所进行的操作, 以此类推
  • import copy,copy.deepcopy(list)

深拷贝

创建深拷贝:

要使用到copy模块。

import copy
l1 = [1, 'b', 'c', [10, 20, 30]]
l2 = copy.deepcopy(l1)

2.示例

?导入模块
import copy
?创建一个列表,包含字符串,整形和列表(可变),并将其深拷贝
list1 = ["str", 123, [111,222]]
list2 = copy.deepcopy(list1)

?查看两个列表的"id",可见是产生了新的一个内存地址
print(id(list1))  #2655395993736
print(id(list2))  #2655397313096
print(id(list1[0]),id(list1[1]),id(list1[2]))
# 2655393364144 140725163491392 2655397312904
print(id(list2[0]),id(list2[1]),id(list2[2]))
# 2655393364144 140725163491392 2655397313672
?针对不可变类型, 拷贝以后任然还是用原来值的内存地址
?针对可变类型, 拷贝之后会申请新的内存地址存放

?当改变"list1"内的第一或者第二个元素(不可变元素)
list1[0] = "aaa"
print(list1[0],id(list1[0]))
# aaa 2655394824496
print(list2[0],id(list2[0]))
# str 2655393364144
?发现"list1"改变的值的内存地址已经变化了,说明是产生的新值,与原来的"str"取消了对应
?而"list2"中的值和"id"都没变,说明""list1"的改变与"list2"无关

?当改变子列表里面的值时
list1[2][0] = 333
print(list1[2],id(list1[2]))
# [333, 222] 2655397312904
print(list2[2],id(list2[2]))
# [111, 222] 2655397313672
?我们发现"list1"的变化依然与"list2"无关

3.总结

  • 深拷贝等于是把源列表和新列表完完整整的独立开来互不干扰,
  • 这样就可以保存原数据, 又可以保存操作后的数据
  • 对于深copy来说,列表是在内存中重新创建的,列表中可变的数据类型是重新创建的,列表中的不可变的数据类型是公用的。
  • 深copy:列表、字典嵌套的数据类型不是同一个

相关面试题

l1 = [1, 2, 3, 4, ['淘小欣']]
l2 = l1[::]
l1[-1].append(666)
print(l2)

相关文章:

  • python小数据池,代码块的深入剖析
  • 软件开发的目录规范
  • Linux前身今世
  • linux操作系统虚拟机安装
  • python之数字类型内置方法
  • python之字符串类型内置方法
  • python之列表类型内置方法
  • python之元组类型内置方法
  • python之字典类型内置方法
  • python集合类型内置方法
  • python文件处理之编码字符
  • python文件处理
  • 函数的基本使用
  • python之迭代器
  • python之三元表达式、生成式、生成器表达式
  • 2017 前端面试准备 - 收藏集 - 掘金
  • CSS盒模型深入
  • Java Agent 学习笔记
  • js学习笔记
  • js正则,这点儿就够用了
  • mysql innodb 索引使用指南
  • node.js
  • React-Native - 收藏集 - 掘金
  • Yii源码解读-服务定位器(Service Locator)
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 高度不固定时垂直居中
  • 聊一聊前端的监控
  • 前端性能优化--懒加载和预加载
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 浅谈sql中的in与not in,exists与not exists的区别
  • #每天一道面试题# 什么是MySQL的回表查询
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • (arch)linux 转换文件编码格式
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (译)2019年前端性能优化清单 — 下篇
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (转)一些感悟
  • ****Linux下Mysql的安装和配置
  • .NET 4.0中的泛型协变和反变
  • .NET Micro Framework初体验(二)
  • .NET开源项目介绍及资源推荐:数据持久层
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • @EnableAsync和@Async开始异步任务支持
  • @EnableWebMvc介绍和使用详细demo
  • @SuppressWarnings注解
  • @Transactional 详解
  • [17]JAVAEE-HTTP协议
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [AHOI2009]中国象棋 DP,递推,组合数
  • [AIGC 大数据基础]hive浅谈