【python基础】—可变可迭代对象修改的问题及浅拷贝深拷贝的问题

news2025/7/14 2:25:01

文章目录

  • 引入:可变可迭代对象修改的问题
    • 1.直接修改可变可迭代对象的问题
    • 2.赋值、浅拷贝和深拷贝三种方式对可变可迭代对象修改问题
  • 一、理解概念
  • 二、赋值
  • 三、浅拷贝
  • 四、深拷贝


引入:可变可迭代对象修改的问题

1.直接修改可变可迭代对象的问题

  • 问题描述:

for循环执行时,获取可迭代对象,会一次性地产生一个迭代器,如果这个迭代器本身是可变的,比如列表,那么使用修改元素的方法,remove()和insert()等等,就会对它产生不可预知的影响,会影响迭代器的迭代。

lst=[1,2,4,3,5]
for i in lst:
    if i % 2 ==0:
        lst.remove(i)
print(lst)

#输出:
#[1, 4, 3, 5]
  • 原因分析:

正如上面这段代码,对for循环生成的迭代器,遍历时我们可以想象是有一个指针不断地往下移动,这里的第一个元素是1,它模2不等于0,继续往下执行,移到第2个元素,第2个元素等于0,因此在列表就把它删除了,删掉后后面的元素会全部往前挪,那么这个时候内存的元素变成了[1, 4, 3, 5],刚刚指针已经移动到2元素这个位置,下一步应该移动向第3个元素,第三个元素其实已经是3了,4这个元素就被跳过了。

  • 解决方案:
lst=[1,2,4,3,5]
for i in lst[:]: # lst[:]是列表浅拷贝,lst[:]与lst是两个不同的对象。
    if i % 2 ==0:
        lst.remove(i)
print(lst)

#输出:
#[1, 3, 5]

2.赋值、浅拷贝和深拷贝三种方式对可变可迭代对象修改问题

  • 问题描述

主要关系到是否会修改原始可变可迭代对象原始可变可迭代对象中的部分对象

  • 举例1
x=[1,2,3]
z=x[:] # 浅拷贝
z[0]=8
print(id(x),x)
print(id(z),z)

# 输出:
# 1865121889792 [1, 2, 3]
# 1865121619776 [8, 2, 3]
  • 举例2
x=[1,2,[3,4]]
y=x[:] # 浅拷贝
y[0],y[2][0]=9,9
print(id(x),x)
print(id(y),y)

# 输出:
# 1865122658880 [1, 2, [9, 4]]
# 1865121574784 [9, 2, [9, 4]]
  • 原因分析

在举例2中,我们发现x的一级元素没有改变,而二级元素改变了,也就是说浅拷贝让一级元素有了自己独立的空间,而二级元素仍然指向了 被拷贝对象(x) 的二级元素的内存区域。
可以理解为,浅拷贝只复制了父对象,就是只复制一级元素而不复制内部子对象,要想同时复制内部子对象与父对象,用copy模块中的深拷贝deepcopy()函数。

  • 解决方案

用copy模块中的深拷贝deepcopy()函数。

import copy
x=[1,2,[3,4]]
z=copy.deepcopy(x)
z[0],z[2][0]=9,9
print(id(x),x)
print(id(z),z)

# 输出:
# 1865121619776 [1, 2, [3, 4]]
# 1865121907200 [9, 2, [9, 4]]

下面主要介绍赋值、浅拷贝、深拷贝对可变对象及不可变对象的应用情况

一、理解概念

  • 变量:是一个系统表的元素,拥有指向对象的连接空间。
  • 对象:被分配的一块内存,存储其代表的值。
  • 引用:是自动形成的从变量到对象的指针。
a = 'hello'

在这里插入图片描述

  • 类型:属于对象,而非变量。
  • 不可变对象:一旦创建就不可以修改的对象,包括字符串、元组、数值类型。
  • 可变对象:可以修改的对象,包括列表、字典、集合。

可变对象与不可变对象,所指向的对象是内存中的值是否可以被改变。

注: id()函数用于获取对象的内存地址。

当改变不可变对象时,由于其值不能被改变,相当于把都原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址
当改变可变对象时,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。

二、赋值

  • 举例1
x=[1,2,3]
y=x #赋值
y[0]=4
print(id(x),x)
print(id(y),y)

# 输出:
# 1865121861120 [4, 2, 3]
# 1865121861120 [4, 2, 3]

我们发现x列表也跟着变化了。因为y和x都是对同一块内存空间的引用,两者的id地址是一致的,所以修改了y,x也会跟着修改。

赋值 只是复制了新对象的引用,不会开辟新的内存空间。

三、浅拷贝

浅拷贝三种方式

  • 切片
lst = [1,2,[3,4]]
lst1=lst[:] 
lst1=[item for item in lst]
  • copy函数
lst = [1,2,[3,4]]
lst1=copy.copy(lst)
  • 工厂函数
lst = [1,2,[3,4]]
lst1=list(lst)

浅拷贝 之所以称为浅拷贝,是它仅仅只拷贝了一层,拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。

情况一

当浅拷贝的值是不可变对象(字符串、元组、数值类型)时,对象id值(id()函数用于获取对象的内存地址)与浅复制原来的值相同。

  • 对不可变对象类型
import copy 
a=(1,2,3)
b=copy.copy(a)
print(a,id(a))
print(b,id(b))
# 输出:
# (1, 2, 3) 1865116784576
# (1, 2, 3) 1865116784576

a=(1,2,3)
b=copy.deepcopy(a) #深拷贝函数,下面讲深拷贝会讲到。
print(a,id(a))
print(b,id(b))
# 输出:
# (1, 2, 3) 1865116784576
#(1, 2, 3) 1865116784576

结论:
不可变对象类型,没有被拷贝说法,即使是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。
所以不可变类型,不管是深拷贝还是浅拷贝,地址值和拷贝后的值都是一样的。

情况二

  • 对可变对象(无子对象)
import copy
a=[1,2,3]
b=copy.copy(a)
print(a,id(a))
print(b,id(b))
# 输出:
# [1, 2, 3] 1865122658688
# [1, 2, 3] 1865121574592

a[0]=8
print(a,id(a))
print(b,id(b))
# 输出:
# [8, 2, 3] 1865122658688
# [1, 2, 3] 1865121574592

结论:
针对复制的对象中无复杂子对象,
原来值的id值与浅拷贝值的id不同。
原来值的发生改变,并不会影响浅拷贝的值,同时浅拷贝的值改变,也并不会影响原来的值。

  • 对可变对象(有子对象)

    • 外层改变元素
import copy
a=[1,2,[1,2,3,4]]
b=copy.copy(a)
a[0]=8
print(a,id(a))
print(b,id(b))

# 输出:
# [8, 2, [1, 2, 3, 4]] 1865121846528
# [1, 2, [1, 2, 3, 4]] 1865121586368
    • 内层改变元素
import copy
a=[1,2,[1,2,3,4]]
b=copy.copy(a)
a[0],a[2][0]=9,9
print(a,id(a))
print(b,id(b))

# 输出:
# [9, 2, [9, 2, 3, 4]] 1865121688192
# [1, 2, [9, 2, 3, 4]] 1865121836672

结论:
针对复制的对象中有复杂子对象,
外层添加元素时,浅拷贝不会随原列表变化而变化;内层添加元素时,浅拷贝才会变化。

四、深拷贝

和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。
所以改变原有被复制对象不会对已经复制出来的新对象产生影响。

深拷贝方式

  • deepcopy函数

情况一

  • 不可变对象

结论: 不可变对象类型,没有被拷贝说法,针对不可变对象的情况已经在浅拷贝情况中介绍,可以参见上面浅拷贝情况一的内容。这里就不重复讨论。

情况二

  • 可变对象(无子对象)
import copy
a=[1,2,3]
b=copy.deepcopy(a)
a[0]=8
print(a,id(a))
print(b,id(b))

# 输出:
# [8, 2, 3] 1865122658432
# [1, 2, 3] 1865121847488
  • 对可变对象(有子对象)

    • 外层改变元素
import copy
a=[1,2,[1,2,3,4]]
b=copy.deepcopy(a)
a[0]=8
print(a,id(a))
print(b,id(b))
# 输出:
# [8, 2, [1, 2, 3, 4]] 1865122632640
# [1, 2, [1, 2, 3, 4]] 1865122658880
    • 内层改变元素
import copy
a=[1,2,[1,2,3,4]]
b=copy.deepcopy(a)
a[0],a[2][0]=9,9
print(a,id(a))
print(b,id(b))

# 输出:
# [9, 2, [9, 2, 3, 4]] 1865121574592
# [1, 2, [1, 2, 3, 4]] 1865122657600

结论:
原来值的id值与深拷贝值的id不同。
无论原列表如何变化,深拷贝都保持不变。


参考文章:

https://zhuanlan.zhihu.com/p/54011712

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

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

相关文章

项目中webpack优化配置(1)

项目中webpack优化配置 一. 开发效率, 体验 1. DLL(开发过程中减少构建时间和增加应用程序的性能) 使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先…

大规模采用奇点临近?Web3应用爆发离不开这个“支撑”赛道

作者|Jason Jiang 数据是当今世界最具价值的资源,也是数字掘金的必争之地。尽管Web3迄今仍有诸多争议,但随着铭文、Gamefi、DeFi等链上生态的多样化发展,我们正身处Web3应用爆发的洪流之中,区块链数据赛道也因此备受关…

Java智慧工地源码 SAAS智慧工地源码 智慧工地管理可视化平台源码 带移动APP

一、系统主要功能介绍 系统功能介绍: 【项目人员管理】 1. 项目管理:项目名称、施工单位名称、项目地址、项目地址、总造价、总面积、施工准可证、开工日期、计划竣工日期、项目状态等。 2. 人员信息管理:支持身份证及人脸信息采集&#…

Java代码审计Mybatis注入文件上传下载读取(非常详细!!)

目录 0x00 前言 0x01 Mybatis注入审计 - 若依(Ruoyi)后台管理系统 4.6.0 1、项目介绍与部署 - Ruoyi 2、若依 Ruoyi - Mybatis注入 - 代码审计 3、代审常搜词 - Java SQL 注入 0x02 文件上传漏洞审计 - Inxedu && Tmall 1、项目介绍与部署…

xposed 01 - 环境搭建

简介 Xposed的作者是rovo89,但是更新完 8.1 的 beta 版之后就不更新了。由于Android新版本的普及,目前新上市的手机基本都是8.0以上。所以Xposed框架已经不适用。EdXposed团队成为Xposed停止更新后的官方接任者。 当然现在有更好的 LSPosed https://git…

Leetcod面试经典150题刷题记录 —— 矩阵篇

矩阵篇 1. 有效的数独2. 螺旋矩阵Python 3. 旋转图像Python额外开辟数组空间原地置换法 4. 矩阵置零5. 生命游戏Python 1. 有效的数独 题目链接:有效的数独 - leetcode 题目描述: 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验…

融资项目——vue之双向数据绑定

上一篇文章中使用的v-bind是单向绑定方法,即数据改变,网页相应的视图发生改变,但是网页视图发生改变其相关联的数据不会发生改变。但是双向数据绑定不同之处在于网页视图发生改变其相关联的数据也会发生改变。Vue可以使用v-model进行双向数据…

深入浅出堆排序: 高效算法背后的原理与性能

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《linux深造日志》 《高效算法》 ⛺️生活的理想,就是为了理想的生活! 📋 前言 🌈堆排序一个基于二叉堆数据结构的排序算法,其稳定性和排序效率在八大排序中也…

数学建模学习笔记-皮尔逊相关系数

内容:皮尔逊相关系数 一.概念:是一个和线性线关的相关性系数 1.协方差概念: 协方差受到量纲的影响因此需要剔除 2.相关性的误区 根据这个结论,我们在计算该系数之前需要确定是否为线性函数 二.相关性的计算 1.Matlab&#xff…

UE4移动端最小包优化实践

移动端对于包大小有着严苛的要求,然而UE哪怕是一个空工程打出来也有90+M,本文以一个复杂的工程为例,探索怎么把包大小降低到最小。 一、工程简介 工程包含代码、插件、资源、iOS原生库工程。 二、按官方文档进行基础优化 官方文档 1、勾选Use Pak File和Create comp…

Prompt-to-Prompt:基于 cross-attention 控制的图像编辑技术

Hertz A, Mokady R, Tenenbaum J, et al. Prompt-to-prompt image editing with cross attention control[J]. arXiv preprint arXiv:2208.01626, 2022. Prompt-to-Prompt 是 Google 提出的一种全新的图像编辑方法,不同于任何传统方法需要用户指定编辑区域&#xff…

【linux】用grep或者pgrep查找进程ID

一、用grep ps aux|grep 字符串|awk {print $2} 像上面这样运行,还会同时显示grep的进程ID。 需要再添加grep的反向查找命令,即查找不含有 "grep" 字段的行:grep -v grep。 ps aux | grep 字符串 | grep -v grep | awk {print …

058:vue组件引用外部js的方法

第058个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…

redis基本用法学习(C#调用NRedisStack操作redis)

redis官网文档中推荐C#中使用NRedisStack包连接并操作redis,本文学习C#调用NRedisStack操作redis的基本方式。   新建Winform项目,在Nuget包管理器中搜索并安装NRedisStack包,如下图所示: 主要调用StackExchange.Redis命名空间下…

苹果如何从iCloud恢复备份?正确方法看这里!

iCloud为所有苹果用户免费提供5G内存空间,用户可以将照片、短信、联系人、备忘录等重要信息备份到iCloud云端,这样可以方便在不同设备之间同步和共享。 同时,iCloud保证这些数据在所有苹果设备上及时自动更新。当遇到手机数据丢失时&#xf…

PDM产品数据管理软件,企业怎么选择PDM软件

PDM产品数据管理软件是用于管理和控制与产品相关的所有数据和信息的一套软件工具。它提供了一个中心化的平台,以组织、存储、共享和管理与产品相关的所有数据,包括设计图纸、文档、工艺流程、产品配置、材料清单(BOM)等。 PDM软件…

MyBatis:动态 SQL 标签

MyBatis 动态 SQL 标签if 标签where 标签trim 标签choose 、when 、otherwise 标签foreach 标签附 动态 SQL 标签 MyBatis 动态 SQL 标签,是一组预定义的标签,用于构建动态的 SQL 语句,允许在 SQL 语句中使用条件、循环和迭代等逻辑。通过使…

福建农林大学 html +css + JavaScript 期末复习 -- 保姆级

html css JavaScript 期末复习&#xff08;保姆级复盘&#xff09; 考试题型 1、选择题 20题 30分 2、判断题 15题 15分 3、程序题 3 题 30分 4、综合题 2 题 25分 1、网页第一代文本标签&#xff08;直接上代码&#xff0c;看保姆级注解&#xff09; <!-- doctype: docum…

基于JAVA的高校学生管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生管理模块2.2 学院课程模块2.3 学生选课模块2.4 成绩管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学院课程表3.2.3 学生选课表3.2.4 学生成绩表 四、系统展示五、核心代码5.1 查询课程5.2 新…

Golang 的内存管理

文章目录 1.内存管理角色1.常见的内存分配方法线性分配器空闲链表分配器TCMalloc 2.Go 内存管理组件mspanmcache初始化替换微分配器 mcentralmheap 3.内存分配4.内存管理思想参考文献 1.内存管理角色 内存管理一般包含三个不同的组件&#xff0c;分别是用户程序&#xff08;Mu…