copy.deepcopy()_python 内存管理

copy.deepcopy()_python 内存管理参考文章http://iaman.actor/blog/2016/04/17/copy-in-python**首先直接上结论:—–我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。—–而浅复制并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

参考文章 http://iaman.actor/blog/2016/04/17/copy-in-python
这里写图片描述

**首先直接上结论: —–我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。
—–而浅复制并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。这就和我们寻常意义上的复制有所不同了。**

对于简单的 object,用 shallow copy 和 deep copy 没区别

复杂的 object, 如 list 中套着 list 的情况,shallow copy 中的 子list,并未从原 object 真的「独立」出来。也就是说,如果你改变原 object 的子 list 中的一个元素,你的 copy 就会跟着一起变。这跟我们直觉上对「复制」的理解不同。

看不懂文字没关系我们来看代码:

>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin 里边有三个元素:1, 2,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False 
#cop1 和 cop2 看上去相同,但已不再是同一个object
>>> origin[2][0] = "hey!" 
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin内的子list [3, 4] 改掉了一个元素,观察 cop1 和 cop2

可以看到 cop1,也就是 shallow copy 跟着 origin 改变了。而 cop2 ,也就是 deep copy 并没有变。

似乎 deep copy 更加符合我们对「复制」的直觉定义: 一旦复制出来了,就应该是独立的了。如果我们想要的是一个字面意义的「copy」,那就直接用 deep_copy 即可。

那么为什么会有 shallow copy 这样的「假」 copy 存在呢? 这就是有意思的地方了。

python的数据存储方式
Python 存储变量的方法跟其他 OOP 语言不同。它与其说是把值赋给变量,不如说是给变量建立了一个到具体值的 reference。

当在 Python 中 a = something 应该理解为给 something 贴上了一个标签 a。当再赋值给 a 的时候,就好象把 a 这个标签从原来的 something 上拿下来,贴到其他对象上,建立新的 reference。 这就解释了一些 Python 中可能遇到的诡异情况:

>> a = [1, 2, 3]
>>> b = a
>>> a = [4, 5, 6] //赋新的值给 a
>>> a
[4, 5, 6]
>>> b
[1, 2, 3]
# a 的值改变后,b 并没有随着 a 变

>>> a = [1, 2, 3]
>>> b = a
>>> a[0], a[1], a[2] = 4, 5, 6 //改变原来 list 中的元素
>>> a
[4, 5, 6]
>>> b
[4, 5, 6]
# a 的值改变后,b 随着 a 变了

上面两段代码中,a 的值都发生了变化。区别在于,第一段代码中是直接赋给了 a 新的值(从 [1, 2, 3] 变为 [4, 5, 6]);而第二段则是把 list 中每个元素分别改变。

而对 b 的影响则是不同的,一个没有让 b 的值发生改变,另一个变了。怎么用上边的道理来解释这个诡异的不同呢?

首次把 [1, 2, 3] 看成一个物品。a = [1, 2, 3] 就相当于给这个物品上贴上 a 这个标签。而 b = a 就是给这个物品又贴上了一个 b 的标签。

这里写图片描述
第一种情况:

a = [4, 5, 6] 就相当于把 a 标签从 [1 ,2, 3] 上撕下来,贴到了 [4, 5, 6] 上。

在这个过程中,[1, 2, 3] 这个物品并没有消失。 b 自始至终都好好的贴在 [1, 2, 3] 上,既然这个 reference 也没有改变过。 b 的值自然不变。
这里写图片描述

第二种情况:

a[0], a[1], a[2] = 4, 5, 6 则是直接改变了 [1, 2, 3] 这个物品本身。把它内部的每一部分都重新改装了一下。内部改装完毕后,[1, 2, 3] 本身变成了 [4, 5, 6]。

而在此过程当中,a 和 b 都没有动,他们还贴在那个物品上。因此自然 a b 的值都变成了 [4, 5, 6]。

搞明白这个之后就要问了,对于一个复杂对象的浅copy,在copy的时候到底发生了什么?
再看一段代码:

>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin 里边有三个元素:1, 2,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False 
#cop1 和 cop2 看上去相同,但已不再是同一个object
>>> origin[2][0] = "hey!" 
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin内的子list [3, 4] 改掉了一个元素,观察 cop1 和 cop2

学过docker的人应该对镜像这个概念不陌生,我们可以把镜像的概念套用在copy上面。

概念图如下:
这里写图片描述

copy对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。

所以说看这里的origin[2],也就是 [3, 4] 这个 list。根据 shallow copy 的定义,在 cop1[2] 指向的是同一个 list [3, 4]。那么,如果这里我们改变了这个 list,就会导致 origin 和 cop1 同时改变。这就是为什么上边 origin[2][0] = “hey!” 之后,cop1 也随之变成了 [1, 2, [‘hey!’, 4]]。

而deepcopy概念图如下:
这里写图片描述

deepcopy的时候会将复杂对象的每一层复制一个单独的个体出来。
这时候的 origin[2] 和 cop2[2] 虽然值都等于 [3, 4],但已经不是同一个 list了。即我们寻常意义上的复制。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/186550.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 功能测试数据测试之因果图分析方法[通俗易懂]

    功能测试数据测试之因果图分析方法[通俗易懂]定义是一种利用图解法分析输入的各种组合情况,从而设计测试用例的方法,它适合于检查程序输入条件的各种组合情况。因果图法产生的背景等价类划分法和边界值分析方法都是着重考虑输入条件,但没有考虑输入条件的各种组合、输入条件之间的相互制约关系。这样虽然各种输入条件可能出错的情况已经测试到了,但多个输入条件组合起来可能出错的情况却被忽视了。如果在测试时必须考虑输入条件的各种组合,则可能的组合数目将是天文数字,因此必须考虑采用一种适合于描述多种条件的组合、相应产生多个动作的形式来进行测试用例的设计,这就需要利用

    2022年8月14日
    2
  • Android–SQLite数据库实现增删改查

    Android–SQLite数据库实现增删改查SQLite–轻量级数据库一般常见于安卓客户端。用于存储一些重要数据,因其操作容易、使用简单的好处博得一大批迷妹。今天就来说一说sqlite一些简单的使用方法。一步一步开始做吧,我先创建个项目,主要是使用sqlite的代码。首先,我需要在项目初次运行时创建数据库并新建一张user表,用来存储username和password两个字段;…

    2022年6月26日
    24
  • java实用工具类——使用java代码实现ftp上传下载工具类

    java实用工具类——使用java代码实现ftp上传下载工具类一、引言小编最近忙着学习项目构架上的一些技术,把实用的工具类整理下,单独放在一个项目。其他项目需要用直接使用maven依赖一下就可以使用了。项目中需要实现上传多张图片,由于多张图片,又担心并发量大。所以小编做了一个负载均衡,把上传后的图片保存到linux上的ftp中去,不了解linux上的ftp小编后期编写个教程。二、实现以下ftp的工具类,前提需要保证你的ftp服务器能够使用ftp客…

    2022年4月29日
    40
  • win10总显示打印机未连接服务器,win10安装打印机一直未响应。。。「建议收藏」

    win10总显示打印机未连接服务器,win10安装打印机一直未响应。。。「建议收藏」Win10安装打印机驱动的方法1.首先将打印机与电脑进行连接,目前大部分打印机都是通过USB数据线与电脑U口进行连接的。在打印机连接完成后,我们需要通过以下方法查看打印机连接状态是否正常:2.从打开的“控制面板”界面中,点击“硬件和声音”栏目中的“查看设备和打印机”按钮进入。3.此时将打开“设备和打印机”窗口,从此界面中就可以找到“未指定”的设备,此设备便是当前所连接的打印机。4.Win10正式版…

    2022年6月6日
    84
  • imread参数_imread函数图片路径matlab

    imread参数_imread函数图片路径matlabimread的函数原型是:Matimread(conststring&filename,intflags=1);Mat是OpenCV里的一个数据结构,在这里我们定义一个Mat类型的变量img,用于保存读入的图像,在本文开始有写到,我们用imread函数来读取图像,第一个字段标识图像的文件名(包括扩展名),第二个字段用于指定读入图像的颜色和深度,它的取值可以有以下几种:

    2022年10月14日
    0
  • pycharm开启自动补全_python代码补全插件

    pycharm开启自动补全_python代码补全插件在使用python时候我们可能更倾向于能够使用到自动补全代码的功能在一段时间的找寻和使用过程中,发现了几种能补全代码的插件和方法吧pycharm中TabNine插件 这个我感觉还能用吧~啧百度kite官网下载kite插件百度aiXcoder插件下载aiXcoder插件GitHub开源项目1.首先第一个TabNine的插件 亲测了一段时间,感觉很一般 如果想体验一下也不是不行 安装插件过程如下: pycharm编译器->File->Settings

    2022年8月27日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号