Python中浅拷贝与深拷贝

这篇具有很好参考价值的文章主要介绍了Python中浅拷贝与深拷贝。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

    Python中的赋值语句没有创建副本对于对象来说,它们只是将名称绑定到对象。对于不可变的对象来说,通常是没有什么区别的。但是,为了处理可变对象或可变对象的集合,我们可能需要一种方法来创建这些对象的“真实副本“。

在本文中,将介绍如何在Python 3中复制或“克隆”对象,以及所涉及的一些注意事项。

注:本教程是用Python 3编写的,但是在复制对象时,Python 2和3并没有什么区别。当有不同时,会在文中指出。

让我们首先看看如何复制Python的内置集合。Python内置的集合是可变的,如列表、数据集和集合都可以通过在现有集合上调用它们的原来函数进行复制:

new_list = list(original_list)
new_dict = dict(original_dict)
new_set = set(original_set)

但是,此方法不适用于自定义对象,而且在此基础上,它只创建浅拷贝…对于复合对象,如列表、数据集和集合,有一个重要的区别:浅拷贝和深拷贝

A 浅拷贝意味着构建一个新的集合对象,然后用对原始集合中的子对象引用填充它。本质上,一个浅拷贝只是一个层次的深度。复制过程不会递归,因此不会创建子对象本身的副本。

A 深拷贝使复制过程递归。这意味着首先构造一个新的集合对象,然后使用递归在原始集合中找到的子对象的副本来填充它。以这种方式复制一个对象会遍历整个对象树,从而创建一个完全独立的原对象及其所有子对象的克隆。

让我们来看看一些例子来证明深拷贝和浅拷贝之间的区别。

浅拷贝

在下面的示例中,我们将创建一个新的嵌套列表,然后将其复制到list()中:

>>> xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> ys = list(xs)  # 开始浅拷贝
这意味着ys将成为一个新的、独立的对象,我们可以通过检查这两个对象来验证这一点:

>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
确认ys是独立对象,让我们设计一个小实验。可以尝试将一个新的子列表添加到原始(xs),然后检查确保此修改不影响副本(ys):

>>> xs.append(['new sublist'])
>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist']]
>>> ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
正如所看到的,达到了预期的效果。

但是,因为我们只创建了一个浅层的副本,ys中存储的原始子对象引用xs.

这些没有复制,只是在复制的列表中再次被引用。

因此,当在xs中,此修改反映在ys也是一样,那是因为两个列表共享相同的子对象。

>>> xs[1][0] = 'X'
>>> xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]
>>> ys
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
如果我们创建了一个深拷贝xs,在第一步中,这两个对象将完全独立。这是物体的浅拷贝和深拷贝之间的实际区别。

现在知道了如何创建一些内置集合类的浅拷贝,并且了解了浅拷贝和深拷贝之间的区别。但是我们仍然希望得到答案是:

如何创建内置集合的深拷贝?
如何创建任意对象(包括自定义类)的副本?
这些问题的答案在Python标准库中的copy模块里。该模块为创建任意Python对象的浅拷贝和深拷贝提供了一个简单的接口。

深拷贝

让我们重复前面的列表复制示例,但有一个重要的区别。这次我们要使用deepcopy()这个方法创建一个列表。

>>> import copy
>>> xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> zs = copy.deepcopy(xs)
当检查xs及其克隆zs我们用了copy.deepcopy(),将看到它们看起来是相同的--就像前面的示例:

>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> zs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
但是,如果对原始对象中的一个子对象进行了修改(xs),将看到此修改不会影响深层副本(zs).

这一次,两个对象,原件和副本都是完全独立的。xs是递归地克隆的,包括它的所有子对象:

>>> xs[1][0] = 'X'
>>> xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
>>> zs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
当你亲身体验这些例子的时候,理解起来就更容易了。

顺便说一句,还可以使用copy模块,copy.copy()函数创建对象的浅副本。

如果需要清楚地表达你正在代码中的某个地方创建一个浅表副本,这个方法非常有用的。

复制任意Python对象
现在我们需要回答的问题是如何创建任意对象(包括自定义类)的副本(浅的和深的),现在让我们看看这个。copy.copy()和copy.deepcopy()函数可用于复制任何对象。我将基于前面的列表举个简单的例子。首先让我们定义一个简单的类:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f'Point({self.x!r}, {self.y!r})'

我加了一个__repr__()实现,这样我们就可以在Python解释器中轻松地检查从该类中创建的对象。

注:上面的示例使用Python3.6f-string构造由__repr__…在Python 2和3.6之前的Python 3版本中,将使用不同的字符串格式表达式,例如:

def repr(self):
return ‘Point(%r, %r)’ % (self.x, self.y)
接下来,我们将创建一个Point实例,使用copy模块浅复制它:

>>> a = Point(23, 42)
>>> b = copy.copy(a)
如果我们检查原件的内容Point对象及其克隆,看到了我们所期望的:

>>> a
Point(23, 42)
>>> b
Point(23, 42)
>>> a is b
False

我们来看一个更复杂的例子。定义另一个类来表示二维矩形:

class Rectangle:
    def __init__(self, topleft, bottomright):
        self.topleft = topleft
        self.bottomright = bottomright

    def __repr__(self):
        return (f'Rectangle({self.topleft!r}, '
                f'{self.bottomright!r})')

同样,首先我们将尝试创建矩形实例的浅表副本:

rect = Rectangle(Point(0, 1), Point(5, 6))
srect = copy.copy(rect)
如果检查原始矩形及其副本,将看到__repr__()正在进行覆盖,浅层复制过程:

>>> rect
Rectangle(Point(0, 1), Point(5, 6))
>>> srect
Rectangle(Point(0, 1), Point(5, 6))
>>> rect is srect
False

还记得前面的列表示例是如何说明深拷贝和浅拷贝之间的区别的吗?我要用同样的方法,在对象层次结构中更深地修改一个对象,然后将在(浅)副本中更改:

>>> rect.topleft.x = 999
>>> rect
Rectangle(Point(999, 1), Point(5, 6))
>>> srect
Rectangle(Point(999, 1), Point(5, 6))
接下来,我将创建原始矩形的深拷贝。然后,我修改一下:

>>> drect = copy.deepcopy(srect)
>>> drect.topleft.x = 222
>>> drect
Rectangle(Point(222, 1), Point(5, 6))
>>> rect
Rectangle(Point(999, 1), Point(5, 6))
>>> srect
Rectangle(Point(999, 1), Point(5, 6))
这一次,深拷贝(drect)完全独立于原始(rect)和浅拷贝(srect).

总结

创建对象的浅拷贝不会复制子对象。因此,副本并不完全独立于原件。
对象的深拷贝将递归地复制子对象。克隆完全独立于原始副本,但是创建深拷贝要慢一些。
类复制任意对象(包括自定义类)。文章来源地址https://www.toymoban.com/news/detail-432952.html

到了这里,关于Python中浅拷贝与深拷贝的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Pandas数据中的浅拷贝与深拷贝

    pandas库主要有两种数据结构DataFrames和Series。这些数据结构在内部用索引数组和数据数组表示,索引数组标记数据,数据数组包含实际数据。现在,当我们试图复制这些数据结构(DataFrames和Series)时,我们实际上是复制对象的索引和数据,有两种方法可以做到这一点,即浅复

    2024年02月09日
    浏览(39)
  • python-浅拷贝(copy)与深拷贝(deepcopy)

    目录 一、前言: 二、深拷贝与浅拷贝的异同:         1.相同点:                2.不同点:         3.形象说明:         注意: 三、浅拷贝:  3.1.1浅拷贝示意图: 3.1.2示意图说明         1.对象与子对象(元素)的关系:         2.对象:         3.元

    2024年02月11日
    浏览(36)
  • Python:列表的浅拷贝与深拷贝

    相关阅读 Python专栏 https://blog.csdn.net/weixin_45791458/category_12403403.html?spm=1001.2014.3001.5482         在python语言中,因为其面向对象的特性,在进行列表拷贝时可能会出现一些意想不到的结果,涉及到列表的浅拷贝和深拷贝相关问题,本文将对其进行总结。         首先我们来

    2024年02月09日
    浏览(37)
  • python基础(5):深入理解 python 中的赋值、引用、拷贝、作用域

    目录 python基础(5):深入理解 python 中的赋值、引用、拷贝、作用域 1、先来看个问题吧: 2、引用 VS 拷贝: 3、增强赋值以及共享引用:

    2024年02月08日
    浏览(47)
  • js中浅拷贝和深拷贝的区别

    在JavaScript中,浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是两种不同的复制对象的方法,它们之间存在一些重要的区别。 浅拷贝(Shallow Copy): 浅拷贝只复制对象的顶层属性,如果属性是对象或数组,则复制的是引用,而不是实际的对象。这意味着,如果更改了复制后的对象

    2024年01月20日
    浏览(34)
  • Verilog force语句详解:FPGA中的信号强制赋值

    Verilog force语句详解:FPGA中的信号强制赋值 在FPGA开发中,时序分析和调试是非常重要的一部分。其中,对于一些信号的调试,我们需要准确地模拟不同的情况来检测其工作状态。这时,Verilog force语句就起到了重要的作用。 force语句可以使信号立即进行强制赋值操作,在仿真

    2024年02月06日
    浏览(39)
  • 详解python列表等对象的赋值和复制(浅拷贝copy()及深拷贝deepcopy()的使用区别与示例)

    python虽然没有指针的概念,但是对象、引用、地址这些内容还是存在的,尤其是像列表对象、某类型对象的引用上,搞清楚对象变量的复制和赋值很有必要,不然容易出现“莫名其妙”的错误。 python中给一个变量 a 赋值列表实际上是创建了一个列表对象,并将该列表的地址赋

    2024年02月04日
    浏览(48)
  • 5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数

    浅拷贝 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。 测试结果: 深拷贝 深拷贝是将

    2024年04月26日
    浏览(40)
  • python中赋值语句教程

    赋值语句创建对象的引用:赋值语句总是创建对象的引用,而不是复制对象。因此,Python中的变量更像是指针,而不是数据储存区域。 变量在首次赋值时会被创建:因此不需要提前声明变量。 变量在引用前必须先赋值:如果引用未赋值的变量会报错 (1. 普通形式 (2. 序列赋

    2024年02月04日
    浏览(41)
  • C++的引用 拷贝赋值和引用赋值

        💯 博客内容:C++的引用 拷贝赋值和引用赋值 😀 作  者:陈大大陈 🚀 个人简介:一个正在努力学技术的准前端,专注基础和实战分享 ,欢迎私信! 💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘 目录 引用  引用

    2024年02月12日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包