python中的浅拷贝与深拷贝区别,Python深浅拷贝

  python中的浅拷贝与深拷贝区别,Python深浅拷贝

  在Python中,对象的赋值和复制(深/浅复制)是有区别的。如果使用时不注意,可能会有意想不到的效果。

  本文将通过简单的例子介绍这些概念之间的区别。

  对象分配

  直接看一段代码:

  will=[Will ,28,[Python , C# , JavaScript]]

  威尔伯=威尔

  打印id(遗嘱)

  打印遗嘱

  打印[遗嘱中ele的id(ele)]

  打印id(wilber)

  打印威尔伯

  打印wilber中ele的id(ele)]

  will[0]=Wilber

  意志[2]。追加( CSS )

  打印id(遗嘱)

  打印遗嘱

  打印[遗嘱中ele的id(ele)]

  打印id(wilber)

  打印威尔伯

  打印wilber中ele的id(ele)]

  代码的输出是:

  让我们分析一下这段代码:

  首先创建一个名为will的变量,该变量指向一个list对象,从第一张图就可以看出所有对象的地址(每次运行的结果可能都不一样)

  然后wilber变量由will变量赋值,wilber变量会指向will变量对应的对象(内存地址),即‘Wilber is Will’,‘Wilber[I]is Will[I]’

  可以理解,在Python中,对象的赋值都是传递对象引用(内存地址)。

  在第三张图中,由于will和wilber指向同一个对象,所以对will的任何修改都会反映到wilber中。

  这里需要注意的是,str是一个不可变的类型,所以修改的时候会替换旧的对象,产生一个新的地址39758496。

  浅拷贝

  我们来看看浅抄的结果:

  导入副本

  will=[Will ,28,[Python , C# , JavaScript]]

  wilber=copy.copy(will)

  打印id(遗嘱)

  打印遗嘱

  打印[遗嘱中ele的id(ele)]

  打印id(wilber)

  打印威尔伯

  打印wilber中ele的id(ele)]

  will[0]=Wilber

  意志[2]。追加( CSS )

  打印id(遗嘱)

  打印遗嘱

  打印[遗嘱中ele的id(ele)]

  打印id(wilber)

  打印威尔伯

  打印wilber中ele的id(ele)]

  结果是:

  分析这段代码:

  首先,仍然使用一个will变量来指向list类型的对象。

  然后复制模块中的浅复制函数copy()对will指向的对象进行浅复制,然后将浅复制生成的新对象赋给wilber变量。

  浅层拷贝创建了一个新对象,在这种情况下是‘Wilber is not will’

  但对于对象中的元素,浅拷贝只使用原元素的引用(内存地址),即‘Wilber[I]is will[I]’。

  当威尔被修改的时候,

  由于列表的第一个元素是不可变的类型,所以对应于will的列表的第一个元素将使用新的对象39758496。

  但是列表的第三个元素是可选类型,修改操作不会生成新的对象,所以will的修改结果会相应的反映给wilber。

  总而言之,当我们使用下面的操作时,就会产生浅层复制的效果:

  使用切片[:]操作

  使用工厂函数(如list/dir/set)

  使用复制模块中的copy()函数。

  深层拷贝

  最后,看一下深层文案:

  导入副本

  will=[Will ,28,[Python , C# , JavaScript]]

  wilber=copy.deepcopy(will)

  打印id(遗嘱)

  打印遗嘱

  打印[遗嘱中ele的id(ele)]

  打印id(wilber)

  打印威尔伯

  打印wilber中ele的id(ele)]

  will[0]=Wilber

  意志[2]。追加( CSS )

  打印id(遗嘱)

  打印遗嘱

  打印[遗嘱中ele的id(ele)]

  打印id(wilber)

  打印威尔伯

  打印wilber中ele的id(ele)]

  代码的结果是:

  分析这段代码:

  首先,也使用一个will变量指向list类型的对象。

  然后通过复制模块中的深度复制函数deepcopy()对will指向的对象进行深度复制,然后将深度复制生成的新对象赋给wilber变量。

  类似于浅层拷贝,深层拷贝也会创建一个新对象,在这种情况下‘Wilber is not will’

  但是,对于对象中的元素,深度复制会重新生成一个副本(有特殊情况,下面会解释),而不是简单地使用原始元素的引用(内存地址)。

  示例中,will的第三个元素指向39737304,而wilber的第三个元素是一个全新的对象3973088,即‘Wilber[2]不是will[2]’

  当威尔被修改的时候,

  由于列表的第一个元素是不可变的类型,所以对应于will的列表的第一个元素将使用新的对象39758496。

  但是列表的第三个元素是可有可无的类型,修改操作不会产生新的对象,但是因为 wilber[2]不是will[2],所以Will will的修改不会影响wilber。

  复制的特殊情况

  实际上,复制有一些特殊情况:

  对于非容器类型(如数字、字符串和其他“原子”类型的对象)没有复制这回事。

  也就是说,对于这些类型, obj是copy.copy(obj), obj是copy.deepcopy(obj)

  如果祖先变量仅包含原子类型对象,则不能对其进行深度复制。请参见下面的示例。

  摘要

  本文介绍了对象的赋值和复制,以及它们之间的区别:

  在Python中,对象的赋值就是传递对象引用(内存地址)。

  使用copy.copy()创建对象的浅表副本。它复制对象,但仍使用对象中元素的原始参考。

  如果需要复制容器对象及其所有元素(包括其子元素),可以使用copy.deepcopy()进行深度复制。

  对于非容器类型(如数字、字符串和其他“原子”类型的对象),没有副本。

  如果祖先变量仅包含原子类型对象,则不能对其进行深度复制。请参见下面的示例。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: