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

news2024/11/16 16:41:16

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

定义

  • 直接赋值:其实就是对象的引用。
  • 浅拷贝:拷贝父对象,不会拷贝对象的内部的子对象。
  • 深拷贝: copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。

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

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

  • 有可能由于循环引用,可能会导致递归循环;
  • 深层次有可能会复制过多的对象;

示例

直接赋值

arr = [1, 2, 3, 4]
arr1 = arr
id(arr), id(arr1)
---------------------
(2226394970240, 2226394970240)

结论:直接赋值只是将对对象的引用直接给了新的变量,没有在堆中构建一个新的对象。
在这里插入图片描述

浅拷贝

使用copy.copy()进行浅拷贝,浅复制可以理解为只对最高层对象进行一个复制,对其它层的对象只是引用。

list1 = [[1, 2, 3], [4, 5, 6]]
list2 = copy(list1)
print(id(list1), id(list2))
print(id(list1[0]), id(list2[0]))
print(id(list1[1]), id(list2[1]))

运行结果如下,可以发现list1list2的内存地址并不相同,但是他们内部元素的地址是相同的,也证明了浅拷贝只对最高层对象进行复制,不会对子对象进行复制。

在这里插入图片描述

画图来看,copy方法只是构建了一个新的容器,其内部对堆中对象的引用被复制了,但是堆中的对象没有任何操作。

在这里插入图片描述

list1 = [[1, 2, 3], [4, 5, 6]]
list1.append("append")
print(list1, list2)

结果如下,证明最高层对象是真正在堆中创建了一个新的对象。

在这里插入图片描述

画图来看,list1list2已经不是一个对象了。

在这里插入图片描述

上面的示例中,我们已经证明了对浅拷贝对象本身的操作是不会相互影响的。现在再看另一个示例

arr = [{1, 2, 3}, "aba", 4, {'a': 1, 'b': 2}]
arr1 = arr[:]

for i, j in zip(arr, arr1):
    print(id(i) == id(j))

在这里插入图片描述

从上面的示例可以看到,采用浅拷贝复制对象后,对象内部的所有子对象本质上都是同一个,那么对它们的操作必定会相互影响。

arr1[0].add(4)
arr1[3]['c'] = 3
arr, arr1

在这里插入图片描述

深拷贝

深拷贝可以理解为多层嵌套浅拷贝,需要调用deepcopy进行复制,示例如下:

list1 = [[1, 2, 3], [4, 5, 6], 7, "a"]
list2 = deepcopy(list1)
for i, j in zip(list1, list2):
    print(id(i), id(j))

在这里插入图片描述

可以看到,复制之后[1, 2, 3], [4, 5, 6]的地址已经不在相同,代表这两个对象都已经在堆中重新创建了一份。画图来看:

在这里插入图片描述

'a'以及7的引用是相同的,原因在于这些基础数字、字母在python启动的时候会进行缓存,在内存中有且只有一份。

源码分析

copy方法

def copy(x):
    """Shallow copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """
	
    # 获取要复制对象的类型
    cls = type(x)
	
    # 获取对象的copier,如果copier不为空则使用它进行复制,本质上来说copier是一个Function对象。
    copier = _copy_dispatch.get(cls)
    if copier:
        return copier(x)

    if issubclass(cls, type):
        # treat it as a regular class:
        return _copy_immutable(x)
	
    # 如果对象的类实现了__copy__方法是调用该方法进行复制
    copier = getattr(cls, "__copy__", None)
    if copier is not None:
        return copier(x)
	
    # 使用序列化的方式进行复制,同样是先查看是否有现有的
    reductor = dispatch_table.get(cls)
    if reductor is not None:
        rv = reductor(x)
    else:
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor is not None:
            rv = reductor(4)
        else:
            reductor = getattr(x, "__reduce__", None)
            if reductor:
                rv = reductor()
            else:
                raise Error("un(shallow)copyable object of type %s" % cls)

    if isinstance(rv, str):
        return x
    return _reconstruct(x, None, *rv)
# 定义了那些类型使用deepcopy_atomic作为copier
d[type(None)] = _deepcopy_atomic
d[type(Ellipsis)] = _deepcopy_atomic
d[type(NotImplemented)] = _deepcopy_atomic
d[int] = _deepcopy_atomic
d[float] = _deepcopy_atomic
d[bool] = _deepcopy_atomic
d[complex] = _deepcopy_atomic
d[bytes] = _deepcopy_atomic
d[str] = _deepcopy_atomic
d[types.CodeType] = _deepcopy_atomic
d[type] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
d[weakref.ref] = _deepcopy_atomic
d[property] = _deepcopy_atomic

# 针对容器类型,在builtsin.py中定义了他们复制方式
d[list] = list.copy
d[dict] = dict.copy
d[set] = set.copy
d[bytearray] = bytearray.copy

# deepcopy_atomic会直接返回原始对象,这就是为什么int、str这种类型调用copy方法会返回原始对象
def _deepcopy_atomic(x, memo):
    return x

deepcopy

# 循环递归进行深拷贝,用dispatch_table保存类型:复制方法,判断每一个对象的类型并找到它的深拷贝方法。
# 利用memo放置循环
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """
	
    # memo用来记录复制过的对象,避免循环引用无限复制
    if memo is None:
        memo = {}
	
    # 获取复制对象的id,检查memo中是否有相同的对象,如果有直接返回
    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y
	
    # 整个复制流程和copy一致,不同点在于使用的复制方法不同,
    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)
    if copier is not None:
        y = copier(x, memo)
    else:
        if issubclass(cls, type):
            y = _deepcopy_atomic(x, memo)
        else:
            copier = getattr(x, "__deepcopy__", None)
            if copier is not None:
                y = copier(memo)
            else:
                reductor = dispatch_table.get(cls)
                if reductor:
                    rv = reductor(x)
                else:
                    reductor = getattr(x, "__reduce_ex__", None)
                    if reductor is not None:
                        rv = reductor(4)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                if isinstance(rv, str):
                    y = x
                else:
                    y = _reconstruct(x, memo, *rv)

    # If is its own copy, don't memoize.
    if y is not x:
        memo[d] = y
        _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y

# 元祖深拷贝
def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
    y = [deepcopy(a, memo) for a in x]
    # We're not going to put the tuple in the memo, but it's still important we
    # check for it, in case the tuple contains recursive mutable structures.
    try:
        return memo[id(x)]
    except KeyError:
        pass
    for k, j in zip(x, y):
        if k is not j:
            y = tuple(y)
            break
    else:
        y = x
    return y
d[tuple] = _deepcopy_tuple

# 字典深拷贝方法
def _deepcopy_dict(x, memo, deepcopy=deepcopy):
    y = {}
    memo[id(x)] = y
    for key, value in x.items():
        y[deepcopy(key, memo)] = deepcopy(value, memo)
    return y
d[dict] = _deepcopy_dict
if PyStringMap is not None:
    d[PyStringMap] = _deepcopy_dict
    
# 列表深拷贝
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
d[list] = _deepcopy_list



if PyStringMap is not None:
    d[PyStringMap] = _deepcopy_dict

列表深拷贝

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
d[list] = _deepcopy_list

这是我们的公众号,满满全是干活,三个信通硕士,一个方向互联网后端开发,一个方向互联网大数据开发,一个立志读博士,努力ing的科研人!分享近期学习知识以及个人总结,一起努力,一起加油!求关注!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150148.html

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

相关文章

SpringBoot过滤器与拦截器

为什么要有过滤器和拦截器? 在实际开发过程中,经常会碰见一些比如系统启动初始化信息、统计在线人数、在线用户数、过滤敏高词汇、访问权限控制(URL级别)等业务需求。这些对于业务来说一般上是无关的,业务方是无需关注的,业务只需…

Ubuntu20.04安装ROS Noetic

一、实验环境准备 1.使用系统:Ubuntu20.04(安装不做赘述,可看我另外一篇博客Ubuntu20.04安装),可到Ubuntu官网下载https://ubuntu.com/download/desktop 2.配置网络,使其可通互联网 二、在Ubuntu20.04上搭建ROS机器人…

树上差分-LCA

树上差分算法分析:练习例题差分的基本思想详情见博客(一维、二维差分): https://blog.csdn.net/weixin_45629285/article/details/111146240 算法分析: 面向的对象可以是树上的结点,也可以是树上的边 结点…

springmvc 文件上传请求转换为MultipartFile的过程

前言: 最近在研究文件上传的问题,所以就写下这个博客,让大家都知道从流转换为MutipartFile的过程,不然你就知道在方法中使用,而不知道是怎么样处理的,是不行的 从DiaspatherServlet说起: 别问为啥,去了解tomcat和servlet的关系,我后面会 写这篇博客的 servlet的生命周期 ini…

[ 数据结构 ] 查找算法--------线性、二分、插值、斐波那契查找

0 前言 查找算法有4种: 线性查找 二分查找/折半查找 插值查找 斐波那契查找 1 线性查找 思路:线性遍历数组元素,与目标值比较,相同则返回下标 /**** param arr 给定数组* param value 目标元素值* return 返回目标元素的下标,没找到返回-1*/public static int search(…

63.Python 调用类的属性和方法

63.调用类的属性和方法 文章目录63.调用类的属性和方法1. 调用属性的语法2.调用类的方法3.总结1. 调用属性的语法 我们根据类创建了一张奥迪A6的小汽车。根据汽车流水线呢,汽车生产完之后,需要移交给检查部门检查车辆的外观、颜色(属性)等是否与图纸一致…

JavaEE高阶---SpringBoot 统一功能处理

一:什么是SpringBoot 统⼀功能处理 SpringBoot统一功能处理是AOP的实战环节。我们主要学习三方面内容: 统一用户登录权限验证;统一数据格式返回;统一异常处理。 二:统一用户登录权限验证 Spring 中提供了具体的实现…

Scala 集合常用函数

文章目录集合常用函数一、基本属性和常用操作1、常用方法2、案例示例二、衍生集合1、衍生集合常用方法操作2、案例示例三、集合简单计算函数1、常用计算操作2、案例示例四、集合计算高级函数1、语义说明2、案例示例集合常用函数 一、基本属性和常用操作 1、常用方法 (1) 获取…

Release库与Debug库混用导致释放堆内存时产生异常的详细分析

目录 1、问题描述 2、使用Windbg启动Debug版本的exe程序进行分析 3、进一步分析 4、问题复盘 5、为什么Debug库与Release库混用可能会出异常? 6、最后 VC常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)ht…

DM8:dexpdimp-逻辑导出--逻辑导入

DM8:dexp&dimp-逻辑导出--逻辑导入1 dexp逻辑导出dmp文件1.1 全库导出命令附加的参数信息1.2 导出用户所拥有权限的数据库对象-命令附加的参数信息1.3 导出用户所拥有权限的数据库对象-命令附加的参数信息2 dimp--逻辑导入dmp文件2.1 全库导入dmp数据文件-命令附加的参数信…

人体姿态估计-论文精读--DeepPose: Human Pose Estimation via Deep Neural Networks

图 1. 除了关节的极端变异性外,许多关节几乎不可见。我们能猜测左图中右臂的位置,因为我们看到了姿势的其余部分,并预测了这个人的运动或活动。同样,右边的人的左半身也根本看不见。这些都是需要进行整体推理的例子。我们相信DNNs…

php处理支付宝应用网关给接口发送的post参数

php如何接收支付宝应用网关发送的POST请求方式,参数又是GET请求的数据格式配置支付宝应用网关如何接收支付宝异步通知(应用网关接收请求)将&连接的参数分割成数组实例:难点配置支付宝应用网关 首先要在服务器上写一个接口,然后将接口的访问地址设置在支付宝应用…

手把手教Arthas,不再怕排查线上问题了

Arthas是alibaba开源的java诊断工具,支持jdk6,采用命令行交互模式,可以防败的定位和诊断线上的程序运行问题。官方文档:https://arthas.aliyun.com/doc/一、Arthas使用场景是否有一个全局视角来查看系统的运行状况?为什…

六种方式,教你在SpringBoot初始化时搞点事情!

前言 在实际工作中总是需要在项目启动时做一些初始化的操作,比如初始化线程池、提前加载好加密证书....... 那么经典问题来了,这也是面试官经常会问到的一个问题:有哪些手段在Spring Boot 项目启动的时候做一些事情? 方法有很多…

卷积层里的多输入多输出通道、池化层

多输入多通道每个通道都有一个卷积核,结果是所有通道卷积结果的和。无论有多少输入通道,到目前为止我们只用到单输出通道。可以有多个三维卷积核,每个核生成一个输出通道。输出通道数是卷积层的超参数。每个输入通道有独立的二维卷积核&#…

为什么JavaScript这么难学啊?

前言 觉得Js难其实是很正常的,首先这证明你在某些知识点上没有理解透彻,JS挺多的知识点点其实是比较抽象的,比如闭包、原型和原型链等,其次便是不会变通运用,这主要是敲代码熟练度的问题,所以我针对你这种…

架构运维篇(六):MySQL 8.0启用BinLog 支持

上一篇:架构运维篇(五):Centos7/Linux中安装RocketMQ 最新线上的项目终于到多个数据执行的问题,找了团队DBA发现云上的MySQL 默认是没有启用BinLog支持。 小编研究了一下很简单,不过中间也遇到一些坑可以给…

结构重参数化(Structural Re-Parameters)PipLine

文章目录BASICSstrcutural Inception算法思想算法核心算法架构Re-Parameter四部曲:ACNetACNet原理ACNet分析涨点原因推理阶段融合机制Re-Parameter四部曲:RepVGGRepVGG原理RepVGG分析RepVGG BlockStructural Re-Parameters融合conv2d和BN融合1x1conv转换…

【一文讲通】样本不均衡问题解决--下

1欠采样、过采样欠采样:减少多数类的数量(如随机欠采样、NearMiss、ENN)。过采样:尽量多地增加少数类的的样本数量(如随机过采样、以及2.1.2数据增强方法),以达到类别间数目均衡。还可结合两者做…

地址解析协议ARP

目录地址解析协议ARP1、流程2、动态与静态的区别3、ARP协议适用范围地址解析协议ARP 如何从IP地址找出其对应的MAC地址? 1、流程 ARP高速缓存表 当主机B要给主机C发送数据包时,会首先在自己的ARP高速缓存表中查找主机C的IP地址所对应的MAC地址&#xf…