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

Python深拷贝(deepcopy)、浅拷贝(copy)、等号拷贝的深入理解----看了还不懂找我

编程中难免会遇到copy(浅拷贝)与deepcopy(深拷贝)问题,那么你知道等号(=)拷贝和浅拷贝(copy)有什么区别吗?也许很少有人对二者的区别能讲出一二三吧,看完这篇文章,你就会知道python的变量存储机制,以及深浅拷贝以及等号拷贝的差异所在了。

对于Python中的拷贝问题我也查阅了不少资料,在网上也没有找到如意的讲解,进过一段时间的深入理解之后,我把理解的内容写在这里,供大家参考,不对的地方欢迎大家批评指正。

首先在python中,什么是浅拷贝,什么是深拷贝呢?什么是等号拷贝(形如:bb = aa)呢?有谁能一句话解释清楚,他们的区别又在哪里?-----一定记住针只有对复杂对象才有区别意义!!!

主要是针对复杂结构对象,复杂结构对象就是嵌套两层及以上的子对象,比如:即列表中嵌套子列表,像[1, [2, 3]]这种结构

复杂对象中的深浅拷贝:

一句话解释(=)等号拷贝:当于对于电脑中某个文件夹新建了一个快捷图标,快捷图标永远和原文件是一致的。

一句话解释(copy)浅拷贝:相当于对于电脑中某个文件夹内部的所有子文件及子文件夹新建了快捷图标,放到新的文件夹中。

一句话解释(deepcopy)深拷贝:相当于对于电脑中某个文件夹用u盘拷贝了一个备份。所以原来电脑中文件夹内文件改变时,u盘的文件是不会变化的。

要弄清楚拷贝原理,首先应该弄清楚Python变量存储机制

Python中变量的存储机制

1. aa = 1的存储机制

当aa = 1 时,首先Python会在内存中新开辟一个空间存储数字“1”,然后将该内容的地址赋值给变量‘aa’。有点像如下图所示

2. bb = aa的复制机制

对于正常的“=” 赋值, 比如 bb = aa,则有

3. or_list = [1, [2, 3]]存储机制

Python中复杂对象,等号拷贝,copy浅拷贝,deepcopy深拷贝机制

1. 拷贝后的差异

看以下代码初始列表为 or_list = [1, [2, 3]],分别进行"="拷贝,copy浅拷贝,deepcopy深拷贝

操作如下,各个拷贝后列表内存位置通过id()可以看到差异

1. 初始列表 or_list = [1, [2, 3]]
2. 进行以下三个复制操作
    eq_list = or_list
    sh_list = copy(or_list)
    de_list = deepcopy(or_list)
3. 各个list的值为:
    or_list = [1, [2, 3]]
    eq_list = [1, [2, 3]]
    sh_list = [1, [2, 3]]
    de_list = [1, [2, 3]]
4.  =====各变量ID的值===========
    id(or_list)= 2269079198528
    id(eq_list)= 2269079198528
    id(sh_list)= 2269076677056
    id(de_list)= 2269076677632
    
5.  ======列表第一个位置的ID值=======  
    id(or_list[0]) = 2269075013872
    id(eq_list[0]) = 2269075013872
    id(sh_list[0]) = 2269075013872
    id(de_list[0]) = 2269075013872


6.  ======列表第二个位置的ID值===========
    id(or_list[1]) = 2269076677312
    id(eq_list[1]) = 2269076677312
    id(sh_list[1]) = 2269076677312
    id(de_list[1]) = 2269077081216

6.1 ====列表第二个位置的子列表第一位置的ID值=====
    id(or_list[1][0]) = 2269075013904
    id(eq_list[1][0]) = 2269075013904
    id(sh_list[1][0]) = 2269075013904
    id(de_list[1][0]) = 2269075013904

6.2 ====列表第二个位置的子列表第二位置的ID值=====
    id(or_list[1][1]) = 2269075013936
    id(eq_list[1][1]) = 2269075013936
    id(sh_list[1][1]) = 2269075013936
    id(de_list[1][1]) = 2269075013936

如果纯文字不好理解,可以参考以下示意图

2. 子对象内的内容被改变 

进行or_list[1][0] = 4操作,结果如下所示:

可以看出原始该位置存储的id是*176,初始列表该位置发生改变时,等号拷贝和copy浅拷贝也跟着发生了改变,但是deepcopy深拷贝没有发生变化。为什么会这样呢?改变的原理可以看下面的示意图。因为deepcopy是在List[1]位置就创建了新的内存空间,其它拷贝并没有,他们还是引用的原始列表相同的内存空间,所以才会跟着一起变化

======更改前:or列表的子列表第二个位置的id值===========
id(or_list[1][0])= 2269075013904
id(eq_list[1][0])= 2269075013904
id(sh_list[1][0])= 2269075013904
id(de_list[1][0])= 2269075013904

进行更改: or_list[1][0] = 4 
======更改后:or列表的子列表第二个位置的ID值===========
id(or_list[1][0])= 2269075013968
id(eq_list[1][0])= 2269075013968
id(sh_list[1][0])= 2269075013968
id(de_list[1][0])= 2269075013904
======更改后变量值===========
or_list_after_change =  [1, [4, 3]]
eq_list_after_change =  [1, [4, 3]]
sh_list_after_change =  [1, [4, 3]]
de_list_after_change =  [1, [2, 3]]

 3. 改变List[0]位置的值时

改变第一层的元素时,只有等号拷贝跟着改变了,是因为copy浅拷贝和deepcopy深拷贝都是新开辟了内存空间,存储List[0],List[1],所以当原始的List[0]发生改变时,这两个拷贝没有任何影响,具体原理查看以下示意图

======更改前:or列表的第一个位置的ID值===========
id(or_list[0])= 2269075013872
id(eq_list[0])= 2269075013872
id(sh_list[0])= 2269075013872
id(de_list[0])= 2269075013872

进行更改: or_list[0] = 5 
======更改后:or列表的第一个位置的ID值===========
id(or_list[0])= 2269075014000
id(eq_list[0])= 2269075014000
id(sh_list[0])= 2269075013872
id(de_list[0])= 2269075013872

======更改后变量值===========
or_list_after_change =  [5, [4, 3]]
eq_list_after_change =  [5, [4, 3]]
sh_list_after_change =  [1, [4, 3]]
de_list_after_change =  [1, [2, 3]]

 4. 改变List[1]位置的值时

原理同3改变List[0]位置的值。原理和代码参考以下代码是示意图

======更改前: or列表的第一个位置的ID值===========
id(or_list[1])= 2269076677312
id(eq_list[1])= 2269076677312
id(sh_list[1])= 2269076677312
id(de_list[1])= 2269077081216

进行更改: or_list[1] = {'1': 'a', 'b': 1}
======更改后: or列表的第一个位置的ID值===========
id(or_list[1])= 2269076306688
id(eq_list[1])= 2269076306688
id(sh_list[1])= 2269076677312
id(de_list[1])= 2269077081216

======更改后变量值===========
or_list_after_change =  [5, {'1': 'a', 'b': 10}]
eq_list_after_change =  [5, {'1': 'a', 'b': 10}]
sh_list_after_change =  [1, [4, 3]]
de_list_after_change =  [1, [2, 3]]

相关文章:

  • 【论文研读】-Defining the Ethereum Virtual Machine for Interactive Theorem Provers
  • HIVE 3 使用 MR 引擎多表关联 (JOIN) 导致丢数的问题复现、问题根源及解决方案 (附代码)
  • 计算机毕业设计Java网上求职招聘系统(源码+系统+mysql数据库+Lw文档)
  • C#构造函数
  • 【Node.js+koa--后端管理系统】用户注册接口设计 | 连接Mysql数据库 | 校验注册权限
  • 30岁年薪28W,我还是没顶住压力跳槽了····
  • boost之string_ref
  • Java实现拼图小游戏(1)—— JFrame的认识及界面搭建
  • Java成品网站推荐 毕设从这起步就够了
  • P4 开发实践 — NG-SDN Tutorial — Exercise 5: IPv6 Routing
  • Android Studio Dolphin | 2021.3.1 发布,快来看看有什么更新吧~
  • 常见软件---SQLite3的C语言下使用
  • 嵌入式C语言(入门必看)
  • 神经网络解决优化问题,神经网络 样本不平衡
  • java 使用curl 超时无返回结果问题 有请求 无响应 卡死问题
  • [rust! #004] [译] Rust 的内置 Traits, 使用场景, 方式, 和原因
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Consul Config 使用Git做版本控制的实现
  • django开发-定时任务的使用
  • Git的一些常用操作
  • hadoop集群管理系统搭建规划说明
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • js算法-归并排序(merge_sort)
  • 爱情 北京女病人
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 基于axios的vue插件,让http请求更简单
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 微服务框架lagom
  • 学习使用ExpressJS 4.0中的新Router
  • 与 ConTeXt MkIV 官方文档的接驳
  • 怎么把视频里的音乐提取出来
  • 转载:[译] 内容加速黑科技趣谈
  • C# - 为值类型重定义相等性
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 昨天1024程序员节,我故意写了个死循环~
  • ​ubuntu下安装kvm虚拟机
  • #NOIP 2014# day.2 T2 寻找道路
  • #大学#套接字
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (安卓)跳转应用市场APP详情页的方式
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (生成器)yield与(迭代器)generator
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (四)图像的%2线性拉伸
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (一)Java算法:二分查找
  • (转)Sql Server 保留几位小数的两种做法
  • .net 逐行读取大文本文件_如何使用 Java 灵活读取 Excel 内容 ?
  • .Net多线程总结
  • .NET文档生成工具ADB使用图文教程
  • @Transactional 详解