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

news2025/2/23 13:57:21

【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 result

a = 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)] = y
    append = y.append
    for 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 result
    
    def __deepcopy__(self, memo):
        print("进行深拷贝")
        cls = self.__class__
        result = cls.__new__(cls)
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            setattr(result, k, deepcopy(v, memo))
        return result
    
a = 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.coloradmin.cn/o/152008.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

架构设计---性能设计的详解

前言: 系统性能是互联网应用最核心的非功能性架构目标,系统因为高并发访问引起的首要问题就是性能的问题,高并发访问的情况下,系统因为资源不足,处理每个请求的时间都会变慢,看起来就是性能的变差。 因此…

什么是随机森林?

什么是随机森林? 随机森林是一种有监督的机器学习算法。由于其准确性,简单性和灵活性,它已成为最常用的一种算法。事实上,它可以用于分类和回归任务,再加上其非线性特性,使其能够高度适应各种数据和情况。 …

JavaSE初阶篇:系统环境变量path|classpath|JAVA_HOME

环境变量是为了 “在命令行窗口下”编译和运行Java程序而配置一、系统变量:Pathpath环境变量作用:将命令所在的路径配置到path中去,就相当于在计算机中“注册”了一样,以后找这个命令,会直接去你配置的路径下寻找。达到…

Revit中怎么使两面墙拉近时不自动连接?柱断梁墙功能

一、Revit中怎么使两面墙拉近时不自动连接? 问题:同材质的墙体在同一条直线上只要将其端点拖拽至一起就会融合成一道整墙体(如图-1、2所示),但是在做一些较特殊的结构时我们不需要其连接成一道整墙,有没有什么办法能够设置呢? 我们可以通过…

避免Mysql插入重复数据的几种方法

1、前言 在平时对数据库操作时,有时候需要向数据库中插入一些数据,此时就需要使用数据库的插入语句,但是在向数据中库插入数据时,不能盲目插入,因为盲目的插入可能会造成数据重复,浪费数据库的资源&#x…

Pytest三种配置文件

😎😎原文出自:测个der,博主的公众号,格式美观一些。 配置介绍 pytest 的主配置文件,可以改变 pytest 的默认行为,执行 pytest -h,这里有很多配置均可用于 pytest.ini配置 (venv) …

HarmonyOS鸿蒙学习笔记(16)Canvas入门使用

Canvas入门指南1、Canvas代码结构2、简单案例2.1 fillStyle和fillRect 绘制蓝色矩形2.2 strokeRect和strokeStyle 绘制红色边框2.3 lineCap的使用2.4 lineJoin的使用2.5 closePath的使用3、参考资料1、Canvas代码结构 在使用Canvas的时候,需要向初始化RenderingCon…

第四十七讲:IPv6基础知识

一、IPv6网络元素及概念 IPv6的网络元素如下图所示。 节点:任何运行IPv6的设备,包括路由器和主机(甚至还将包括PDA、冰箱、电视等)。邻节点:连接到同一链路上的节点。这是一个非常重要的概念,因为IPv6的邻…

JS面试题--this指向+面试题

this其他补充 内置函数的绑定思考 01_一些函数的this分析 // 1.setTimeout原理 // function hySetTimeout(fn, duration) { // fn.call(window) // }// hySetTimeout(function() { // console.log(this) // window // }, 3000)// setTimeout(function() { // console.l…

EEG论文阅读和分析:《Differential entropy feature for EEG-based emotion classification》

论文阅读《Differential entropy feature for EEG-based emotion classification》 论文的核心是提出差分熵作为特征,并且对差分差分熵和比例差分熵等特征进行对比研究。 算法流程步骤: 采样过程: A.预处理 根据受试者的压力反应&#xf…

零基础转行软件测试可行吗?

如今,随着人们对软件质量的要求越来越高,软件测试已经成为最热门的IT行业之一。不少非科班出身的转业者也纷纷开始观望这个行业,因此不少人会问,零基础转行软件测试有前途吗?答案显而易见,如果你想成为最炙…

【C语言】数据类型(基本类型、构造类型、类型转换)

⏰打卡:DAY1 🏆今日学习目标:数据类型(基本类型、构造类型、类型转换) ✨个人主页:颜颜yan_的个人主页 🎉专栏系列:从0开始学C语言 文章目录前言基本类型整型浮点型字符型构造类型数…

Mybatis学习——增删改(mysql8.0)

目录 一、配置文件 (一)添加mybatis依赖: (二)resources资源目录的配置 1.database.properties文件的设置 2.mybatis-config.xml文件的设置 二、引入原始类 三、DogDao接口,定义增删改功能 (一)单条数据增删改 1.新增 2.删除 3.修改 (二)多条…

Makefile详细教程

Makefile详细教程 我编写本指南是因为我永远无法完全理解 Makefile。 他们似乎充斥着隐藏的规则和深奥的符号,提出简单的问题并没有得到简单的答案。 为了解决这个问题,我花了几个周末的时间坐下来阅读所有关于 Makefile 的内容。 我已将最关键的知识浓…

【链表】leetcode206.反转链表(C/C++/Java/Js)

leetcode206.反转链表1 题目2 思路2.1 双指针法(迭代)2.2 递归法2.2.1 递归--从前往后翻转指针指向2.2.2 递归--从后往前翻转指针指向3 代码3.1 C版本(迭代)3.2 C版本(迭代递归)3.3 Java版本(迭…

【OpenDDS开发指南V3.20】第十章:Java Bindings

介绍 OpenDDS提供JNI绑定。Java应用程序可以像C++应用程序一样使用完整的OpenDDS中间件。 有关入门的信息,包括先决条件和依赖项,请参阅$DDS_ROOT/java/INSTALL文件。 Java版本9和更高版本使用Java平台模块系统。要在这些Java版本中使用OpenDDS,请将MPC特性Java_pre_jpms…

QT Echarts 联动共享数据表图 使用详解

Echarts是百度的一款可视化界面开发的平台,里面的地图以及数据可视化内容十分丰富,适合用于一些大屏数据显示项目或者一些ui界面开发。每一个ECharts图表使用一个无边框的QWebView来展示,这样多个不同类型的ECharts图表就是多个封装不同类型E…

kettle 筛选数据 并根据关键字段去重 设计

文章目录前言kettle 筛选数据 并根据关键字段去重 设计实现:1、配置sqlite 数据库链接2、先从test表里抽取数据3、将表输入查询的数据插入到excel里4、将筛选出来的数据根据id去重5、插入本地excel6、ETL 整体效果:7、测试:前言 如果您觉得有用的话,记得给博主点个赞…

安全轻量化股票看盘盯盘软件需要实现的功能和基本要求是什么?

有很多投资者是上班族的,因此是不能无时无刻盯盘看盘的,那么为了解决这个问题就需要用上轻量化股票看盘盯盘软件,那么一个安全的轻量化股票看盘盯盘软件需要具备哪些功能和基本要求呢?接下来小编为大家分析分析! 1.一定…

小试跨平台局域网文件传输工具NitroShare,几点感想

随着电脑系统国产化的推进,单位用的OA系统已转移到国产电脑上了,但是国产电脑上的操作系统基于Linux,软件商店里可选的应用软件还不够多,功能也还有待提高。为了提高处理效率,经常需要把文件从国产电脑传到Windows平台…