【Python】python深拷贝和浅拷贝(二)

news/2024/11/27 23:43:26/

【Python】python深拷贝和浅拷贝(二)

前言

上一期我们介绍了Python中深拷贝和浅拷贝的定义以及它们在执行过程中内存结构,同时也给出了深拷贝和浅拷贝的方法。(没有看上一期的朋友看这里,python深拷贝和浅拷贝(一))这一期我们将介绍一下在Python中自定义类型该如何控制深拷贝和前拷贝的行为。

自定义浅拷贝行为

上一期我们介绍了进行一个对象的潜拷贝需要调用copy库中的copy方法。在代码底层会经历5个步骤:

  1. 获取对象的类,这一点可以理解为调用__class__魔术方法;
  2. 在已有的【_copy_dispatch】中获取现有的copier;
  3. 如果在【_copy_dispatch】中没有现有的copier,那么先判断他是否是元类(MetaClass)创建的对象,即类(Class),如果是则调用_copy_immutable方法,处理方式和不可变对象一致;
  4. 如果上述copier都是None,则检查对象的__copy__方法,如果存在该方法则调用该方法;
  5. 使用序列化的方式进行拷贝…

现在定义一个空类并调用copy方法,看看会发生什么:

class A:def __init__(self) -> None:self.product_list = [1, 2, 3, 4]self.dict = {1: 'a', 2: 'b'}a = A()
a_copier = copy(a)
print(id(a.list), id(a_copier.list))
print(id(a.dict), id(a_copier.dict))    

在这里插入图片描述

从上图可以看到,如果没有定义相关拷贝方法,使用copy方法对自定义类的对象进行拷贝将会直接执行步骤5

在这里插入图片描述

从程序的运行结果可以看到复制的对象和原始对象的内存位置不同,但是内部元素地址都是相同的,也就是说如果不实现__copy__魔术方法,那么他的行为和其他内置对象例如列表的浅拷贝行为一致,这种策略符合了python对浅拷贝的定义。

浅拷贝:构造一个新的对象,尽可能的将原始对象中的所有找到的对象引用加入到新构造的对象中;

如果想要控制对象浅拷贝的行为,需要在类中实现__copy__魔术方法,现在重新写一个类:

class A(object):def __init__(self):self.list = [1, 2, 3, 4]self.set = {1, 2, 3, 4}self.dict = {1: 'a', 2: 'b', 3: 'c'}def __copy__(self):print("进行浅拷贝")cls = self.__class__result = cls.__new__(cls)result.__dict__ = {k: v for k, v in self.__dict__.items()}return resulta = A()
a_copied = copy(a)
print(id(a), id(a_copied))
print(id(a.list), id(a_copied.list))
print(id(a.set), id(a_copied.set))
print(id(a.dict), id(a_copied.dict))

我们在A这个类中重写了__copy__方法,此时浅拷贝有三个步骤

  1. 通过__class__方法获取对象的类(class);
  2. 通过调用__new__方法创建了一个新对象;
  3. 将被拷贝对象的__dict__属性通过字典推导式赋予新对象的__dict__属性;

在这里插入图片描述

从程序结果看,a和a_copied已经是两个对象了,但是它们内部的属性依旧是指向同一个子对象。

自定义深拷贝行为

上一期介绍到想要进行深拷贝需要调用copy库中的deepcopy方法。在代码底层和copy方法一致,只不过是从【_deepcopy_dispatch】和【dispatch_table】中获取相应的拷贝方法对象,下面的代码以【list】对象的深拷贝为例进行介绍:

def _deepcopy_list(x, memo, deepcopy=deepcopy):y = []memo[id(x)] = yappend = y.appendfor a in x:append(deepcopy(a, memo))return y

列表的深拷贝就是嵌套的对列表中每一个元素进行深拷贝,同时采用memo这个字典对象进行记录。符合Python对深拷贝的定义。

深拷贝:构造一个新的对象,然后递归的在原始对象中将找到的对象的副本插入其中。

接下来将会以画图的方式以一个列表做对象进行演示

from copy import copy, deepcopy
list1 = [1, 2, [3, 4]]
list1_deepcopied = deepcopy(list1)

在这里插入图片描述

深拷贝完毕之后【list1】和【list1_deepcopied】的内存分布如下:

在这里插入图片描述

最后如果想要控制对象深拷贝的行为,需要重写__deepcopy__方法,以浅拷贝的类为例:

class A(object):def __init__(self):self.list = [1, 2, 3, 4]self.set = {1, 2, 3, 4}self.dict = {1: 'a', 2: 'b', 3: 'c'}def __copy__(self):print("进行浅拷贝")cls = self.__class__result = cls.__new__(cls)result.__dict__ = {k: v for k, v in self.__dict__.items()}return resultdef __deepcopy__(self, memo):print("进行深拷贝")cls = self.__class__result = cls.__new__(cls)memo[id(self)] = resultfor k, v in self.__dict__.items():setattr(result, k, deepcopy(v, memo))return resulta = A()
a_copied = deepcopy(a)
print(id(a), id(a_copied))
print(id(a.list), id(a_copied.list))
print(id(a.set), id(a_copied.set))
print(id(a.dict), id(a_copied.dict))

基本步骤和浅拷贝基本一致,需要注意的点如下:

  1. 定义一个memo字典放置循环引用的无限复制;
  2. 通过被拷贝对象的__dict__属性递归进行新对象的属性设置,本质还是对所有子对象都进行深拷贝。

运行结果如下,可以看到所有的子对象地址都不再相同。

在这里插入图片描述

如果我们查看【list】属性的0号元素,可以发现他们的地址是相同的,具体原因可以看上一期源码分析,针对整数、字符串这种不可变类型,copier的处理方式都是直接将原始对象引用直接返回。

print(id(a.list[0]), id(a_copied.list[0]))

在这里插入图片描述


http://www.ppmy.cn/news/10928.html

相关文章

基于javascript专题一总结(2023年版)

目录 函数柯里化 定义 #实现 #应用场景 #参数复用 Nodejs的EventEmitter #Api #基本使用 #手动实现EventEmitter #JavaScript自定义事件 防抖 浅拷贝和深拷贝 数组去重,扁平,最值 去重 #Object #indexOf filter #Set #排序 #去除重复…

canvas入门教学(5)运动小球屏保特效与下雪特效渲染

本节我们来学习两个例子,第一个例子是如下图这样的,全屏各色各样的小球随机运动,碰撞到屏幕边缘再反弹回来的特效,我们一步一步带着大家来学习这个canvas应用。 首先呢,基于上一个教程的例子,我们需要基础的构建圆, 上节教程在这里 并且呢我们要重复的多次的构建半径…

Vue技术教程(2023-1-9)

第一章:Vue概述 1.hello World Vue:易学易用 性能出色 适用场景丰富的web前端框架vue是一款构建用户界面的JavaScript框架 它基于标准的html css JavaScript构建 并提供了一种声明式 组件化的编程模型 帮助你高效的开发 用户界面 无论是简单还是复杂的…

第三十篇:稳定性容量规划方法

前言在谈容量规划之前,首先要知道我们的系统处理能力有限的,不可能是无限的,处理能力的限制取决于资源的限制,生活中很多这种例子,例如喝水的水杯,也是有容量限制的,超出容量限制,则…

拿捏几道经典的字符串模拟问题

希望本篇对你有所帮助 我发现这种字符串的问题其实写起来很麻烦,可能思路不难多少都能想到一些,主要就是代码的处理,细节问题。太考验代码编写的能力了。这两天写了好多道字符串,模拟之类的问题,今天就分享分享吧 刚…

linux系统中利用QT实现绘制图和图标的方法

大家好,今天主要和大家聊一聊,如何使用QT进行绘图和图标的方法。 第一:绘图和图表简介 绘图与图表在嵌入式里有的比较多,尤其是图表,我们常在股票里看到的“图表折线/曲线图/饼状图等”都可以用 Qt 的图表来实现。绘图…

Java基础算法每日5道详解(3)

136. Single Number 单号 Given a non-empty array of integers nums, every element appears twice except for one. Find that single one. You must implement a solution with a linear runtime complexity and use only constant extra space. 给定一个非空整数数组 nu…

shell原理及Linux权限

shell及Linux权限 目录shell及Linux权限一、指令1.tar指令(重要)2.热键3.bc命令4.uname –r指令:5.关机6.以下命令作为扩展:二.shell命令以及运行原理三.权限1.权限的概念:2.Linux下有两种用户:超级用户(ro…