Python copy()与deepcopy()方法之间有什么区别

 更新时间:2022年10月12日 17:14:18   作者:Python热爱者  
这篇文章主要介绍了Python中的copy()和deepcopy(),下面详细介绍该内容并附上详细代码,需要的朋友可以参考一下文章的具体内容,希望对你有所帮助

前言

copy()与deepcopy()之间的区分必须要涉及到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了。即我们寻常意义上的复制。

到此这篇关于Python copy()与deepcopy()方法之间有什么区别的文章就介绍到这了,更多相关Python copy()与deepcopy()内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python如何将自己的包上传到PyPi并可通过pip安装的方法步骤

    python如何将自己的包上传到PyPi并可通过pip安装的方法步骤

    本文主要介绍了python如何将自己的包上传到PyPi并可通过pip安装的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • keras中模型训练class_weight,sample_weight区别说明

    keras中模型训练class_weight,sample_weight区别说明

    这篇文章主要介绍了keras中模型训练class_weight,sample_weight区别说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • Python使用filetype精确判断文件类型

    Python使用filetype精确判断文件类型

    判断文件类型在开发中非常常见的需求,怎样才能准确的判断文件类型呢?首先大家想到的是文件的后缀,但是非常遗憾的是这种方法是非常不靠谱的,因为文件的后缀是可以随意更改的,而python中有个小插件可以实现,下面我们就来详细探讨下
    2017-07-07
  • 详解Python给照片换底色(蓝底换红底)

    详解Python给照片换底色(蓝底换红底)

    这篇文章主要介绍了详解Python给照片换底色(蓝底换红底),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • python 进程池的两种不同实现方法示例

    python 进程池的两种不同实现方法示例

    这篇文章主要为大家介绍了python 进程池的两种不同实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Python编程实现微信企业号文本消息推送功能示例

    Python编程实现微信企业号文本消息推送功能示例

    这篇文章主要介绍了Python编程实现微信企业号文本消息推送功能,结合实例形式分析了Python微信企业号文本消息推送接口的调用相关操作技巧,需要的朋友可以参考下
    2017-08-08
  • Python实现地图可视化folium完整过程

    Python实现地图可视化folium完整过程

    Folium是一个基于leaflet.js的Python地图库,其中,Leaflet是一个非常轻的前端地图可视化库,本文重点给大家介绍Python实现地图可视化folium完整过程,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • 4种Python基于字段的不使用元类的ORM实现方法总结

    4种Python基于字段的不使用元类的ORM实现方法总结

    在 Python 中,ORM(Object-Relational Mapping)是一种将对象和数据库之间的映射关系进行转换的技术,本文为大家整理了4种不使用元类的简单ORM实现方式,需要的可以参考下
    2023-12-12
  • python中__init__方法知识点详解

    python中__init__方法知识点详解

    Python中常会看到在很多类中都有一个__init__函数,该函数有什么作用,又该如何使用呢,下面这篇文章主要给大家介绍了关于python中__init__知识点的相关资料,需要的朋友可以参考下
    2023-04-04
  • Python中函数相关的变量作用域

    Python中函数相关的变量作用域

    这篇文章主要介绍了Python中函数相关的变量作用域,变量的作用域是指程序代码能够访问该变量的区域,如果超出该区域,在访问时就会出现错误,需要的朋友可以参考下
    2023-08-08

最新评论