Python中赋值、引用、深浅拷贝的区别和联系
文章目录
- 一、对象的唯一id
- 二、赋值
- 三、可变对象和不可变对象
- 四、函数的参数传递
- 五、深拷贝和浅拷贝
- 六、举个栗子
- 6.1 不可变对象的拷贝
- 6.2 可变对象的拷贝
- 6.3 可变对象改变外层元素
- 6.4 可变对象改变内层元素
- 七、总结
一、对象的唯一id
python中的所有对象都有自己的唯一id,id在创建对象时就已经分配给对象,id是对象的内存地址,并且在每次运行程序时都不相同(除了某些具有恒定唯一id的对象,比如-5~256之间的整数)。
id():返回对象的唯一id,适用于python中的任何对象,如变量、字符串、列表、字典、元胞等。
a = 5
b = 'hello'
c = (1, 2, 3)print('>>> id(a):', id(a))
print('>>> id(b):', id(b))
print('>>> id(c):', id(c))
运行三次的结果:
从上图可以看出,整数5的id一直不变,但另外两个变量id的每次重新运行程序的结果都不一样。
二、赋值
python中的赋值语句总是建立对象的引用值,而不是简单的复制对象。因此python变量更像是指针,而不是数据存储区域。如下例子:
import numpy as npa = [1, 2, 3, 4, 5]
def fun(data_in):print(data_in, ' >>>id=', id(data_in))b = data_inprint(b, ' >>>id=', id(b))b[0] = 99print(b, ' >>>id=', id(b))print(data_in, ' >>>id=', id(data_in))return bprint(a, ' >>>id=', id(a))
b = fun(a)
print(a, ' >>>id=', id(a))
print(b, ' >>>id=', id(b))
运行结果:
【解释】:在fun()函数中直接用等号赋值语句对b进行操作,本质上是将data_in的地址赋给b,因此主函数中的变量a和fun()函数中的变量b同时指向了同一个地址,因此在fun()函数中对变量b的所有操作都影响到了主函数中的变量a!
三、可变对象和不可变对象
-
可变对象包括:列表(list)、集合(set)、字典(dict);
-
不可变对象:整数(int)、浮点型(float)、字符串(str)、布尔型(bool)、元胞(tuple)。
判断一个对象是否是可变对象,关键是看操作对象前后的内存地址是否发生变化!如果对某个变量进行了操作,但操作前后的内存地址不变,说明这个变量是可变对象;否则这个变量是不可变对象。
可变与不可变的关键是对象内容能否被修改,而不是对象的指向能否被修改!如下例子:
a = 1
print(a, '>>>id=', id(a))
a = 2
print(a, '>>>id=', id(a))
运行结果:
【解释】:a首先被赋值一个整数,然后再被赋值为另一个整数,到这里很多人会说了“int是可变对象”,那可就大错特错了!注意看可以发现两次输出的a变量的内存地址是不同的,说明第一次给a赋值为一个整数1,a指向了第一个内存地址address1,第二次再给a赋值为另一个整数2,a指向了第二个内存地址address2,但“address1和address2中存放的内容分别是1和2”这个事实是不变的,所以先后两次赋值并不是改变了对象的内容,只是第二次赋值时创建了一个新对象,a指向了这个新对象而已。也就是说,变量a改变的只是指向,而不是内容,所以a是不可变对象。
四、函数的参数传递
-
值传递:指在调用函数时将实际参数复制一份传递到函数中,在函数中对参数进行修改不会到影响实际参数;
-
引用传递:只在调用函数时将实际参数的地址传递到函数中,在函数中对参数进行的修改将影响到实际参数。
python既支持值传递,也支持引用传递。解释器会查看对象引用(即对象的内存地址)指向的变量的类型,如果变量是不可变对象,那么函数参数作为值传递;如果变量是可变对象,那么函数参数作为引用传递。对于值传递的传参方式,函数结束之后主函数中该变量值不发生变化;对于引用传递的传参方式,函数之后主函数中该变量值会发生变化。如下例子:
def fun(p1, p2):p1 = 1p2.append(2)returna, b = 0, [1]
print(a, b)
fun(a, b)
print(a, b)
运行结果:
【解释】:fun()函数的参数p1的传入为a(是整数),是不可变对象,所以p1是值传递,函数fun()运行结束后主函数中的a不发生变化;参数p2的传入为b(是列表),是可变对象,所以p2是引用传递,p2指向变量b指向的内存地址,所以当p2发生变化时同时会改变变量b的取值,函数fun()运行结束后b发生改变。
不要使用可变对象作为函数默认参数!!!比如,下面例子:
def fun(a, b=[]):b.append(a)return bprint(fun(0))
print(fun(0))
print(fun(0))
运行结果:
【解释】:因为函数fun()的第二个参数是可变对象,所以并不是每次调用fun()函数时参数b的传入都是空列表。
为了避免此类问题,可以使用如下代码代替:
def fun(a, b=None):if b is None:b = []b.append(a)return bprint(fun(0))
print(fun(0))
print(fun(0))
五、深拷贝和浅拷贝
.copy()是浅拷贝,.deepcopy()是深拷贝。
相同点:两个操作都会创建一个新的对象,新对象的id都和原始对象的id不同;
本质区别:拷贝出来的对象的id不同,即内存地址不同。
import copya = [1, 2, 3, 4, 5]
b = a # 直接用等号进行复制,相当于引用
c = a.copy()
d = copy.copy(a)
e = copy.deepcopy(a)print(a, '>>>id=', id(a))
print(b, '>>>id=', id(b))
print(c, '>>>id=', id(c))
print(d, '>>>id=', id(d))
print(e, '>>>id=', id(e))
print()print(a[1], '>>>id=', id(a[1]))
print(b[1], '>>>id=', id(b[1]))
print(c[1], '>>>id=', id(c[1]))
print(d[1], '>>>id=', id(d[1]))
print(e[1], '>>>id=', id(e[1]))
运行结果:
import copya = [1, {}, 3, 4, 5]
b = a # 直接用等号进行复制,相当于引用
c = a.copy()
d = copy.copy(a)
e = copy.deepcopy(a)print(a, '>>>id=', id(a))
print(b, '>>>id=', id(b))
print(c, '>>>id=', id(c))
print(d, '>>>id=', id(d))
print(e, '>>>id=', id(e))
print()print(a[1], '>>>id=', id(a[1]))
print(b[1], '>>>id=', id(b[1]))
print(c[1], '>>>id=', id(c[1]))
print(d[1], '>>>id=', id(d[1]))
print(e[1], '>>>id=', id(e[1]))
运行结果:
【解释】:
-
b是由a直接赋值得来的,所以是引用,b的所有属性都和a完全一致,所以a和b的id一致,且a[1]和b[1]的id一致;
-
c和d是由a浅拷贝得来,所以c, d和a的id不同,但c, d的子对象和a的子对象的id相同;
-
e是由a深拷贝得来,所以e和a已经完全没有关系,对象e和对象a的id不同且两个对象的每个可变子对象的id也不同;
-
当a[1]=2时,a[1]是不可变对象,所以不管是深拷贝还是浅拷贝,拷贝得来的对象的第一个元素的id都和a[1]相同;
-
当a[1]={}时,a[1]是可变对象,所以深拷贝对象e的子对象e[1]的id和a[1]的id不同。
六、举个栗子
6.1 不可变对象的拷贝
import copya = (1, 2, 3)
b = a
c = copy.copy(a)
d = copy.deepcopy(a)print(a, '>>>id(a)=', id(a))
print()
print('-------赋值/引用-------')
print(b, '>>>id(a)=', id(b))
print()
print('-------浅拷贝-------')
print(c, '>>>id(a)=', id(c))
print()
print('-------深拷贝-------')
print(d, '>>>id(a)=', id(d))
运行结果:
由于a是不可变对象,那么赋值、深浅拷贝之后的内存地址都和原对象相同,即使是被重新赋值,也只是新开辟了一块内存并让a对象指向了新赋值元素,并不改变原有地址内的内容。
6.2 可变对象的拷贝
import copya = [1, 2, 3]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)print(a, '>>>id(a)=', id(a))
print()
print('-------赋值/引用-------')
print(b, '>>>id(b)=', id(b))
print()
print('-------浅拷贝-------')
print(c, '>>>id(c)=', id(c))
print()
print('-------深拷贝-------')
print(d, '>>>id(d)=', id(d))
运行结果:
Python任何时候的赋值都相当于引用,所以b和a的id相同;由于a是可变对象,所以深浅拷贝得到的新对象c和d的id和a不同。
6.3 可变对象改变外层元素
import copya = [1, 2, [3, 4]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a.append(5)print(a, '>>>id(a)=', id(a))
print()
print('-------赋值/引用-------')
print(b, '>>>id(b)=', id(b))
print()
print('-------浅拷贝-------')
print(c, '>>>id(c)=', id(c))
print()
print('-------深拷贝-------')
print(d, '>>>id(d)=', id(d))
运行结果:
Python任何时候的赋值都相当于引用,所以b和a的元素和id完全相同;由于a是可变对象,因此深浅拷贝之后id都发生变化;由于改变的是a外层元素,而深浅拷贝都拷贝了外层对象,所以改变a的外层元素不影响c和d的id。
6.4 可变对象改变内层元素
import copya = [1, 2, [3, 4]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a[2].append(5)print(a, '>>>id(a)=', id(a))
print()
print('-------赋值/引用-------')
print(b, '>>>id(b)=', id(b))
print()
print('-------浅拷贝-------')
print(c, '>>>id(c)=', id(c))
print()
print('-------深拷贝-------')
print(d, '>>>id(d)=', id(d))
运行结果:
Python任何时候的赋值都相当于引用,所以b和a的元素和id完全相同;由于a是可变对象,所以c和d的id和a不相同;由于浅拷贝只拷贝外部对象,对于内部对象只拷贝了元素引用,所以当a的内部对象a[2]发生改变时,c[2]的元素也会对应发生改变。
七、总结
- Python中的赋值即引用,进行赋值时不会开辟新的内存空间,也不会产生一个新的变量单独存在,只是在原有数据块上打上了一个新标签。当数据块的任意一个标签发生变化时,本质是这个数据块发生变化,那么指向这个数据块的任意标签都会发生变化。
- 浅拷贝常见的形式:切片a=a[:]、工厂函数a=list(a)、copy函数a=a.copy()或a=copy.copy(a)。浅拷贝只拷贝了最外层的对象,子对象只是被拷贝了元素的引用(即对象内的元素没有被拷贝);
- 深拷贝只有一种实现形式:a=copy.deepcopy(a)。深拷贝既拷贝了对象,也拷贝了多层的嵌套子元素,深拷贝得到的对象是一个完全全新的对象,和原对象不再有任何关联。
(本文完整的pdf请关注“张张学算法”,并回复“015”获取~)
相关文章:

Python中赋值、引用、深浅拷贝的区别和联系
文章目录一、对象的唯一id二、赋值三、可变对象和不可变对象四、函数的参数传递五、深拷贝和浅拷贝六、举个栗子6.1 不可变对象的拷贝6.2 可变对象的拷贝6.3 可变对象改变外层元素6.4 可变对象改变内层元素七、总结一、对象的唯一id python中的所有对象都有自己的唯一id&#…...

春招冲刺(十一):前端面试之网络总结
网络总结 Q1: GET和POST的请求的区别 应用场景:Get是一个幂等请求,一般用于请求资源。post不是幂等请求,一般用于修改资源。缓存:Get请求一般缓存,Post一般不缓存报文格式:Get请求体一般为空,…...
Mybatis插件
插件使用 动手实现plugin 首先我们需要实现一下这个Interceptor,其中plugin和setProperties方法可以不实现,plugin是因为已经有了完善的逻辑,而setProperties,如果不需要在intercept()中使用属性,也可以不设置。然后…...
计算机学科专业基础综合科目(408)
文章目录408 第一章 数据结构数据是客观事物的符号表示,是对现实世界的事物采用计算机能够识别,存储和处理的形式进行描述的符号的集合。 数据元素是数据的基本单位。一个数据元素由若干个数据项组成。数据项又成为简单数据项及复合数据项两种。简单数据…...

centos7安装教程
1.点击文件–新建虚拟机 2.根据图片一直下一步或者做一些改动 3. 点击自定义硬件,点击浏览选中下载好的ISO文件 4.配置完成后启动虚拟机 5.选择语言,中英文都可,按需求选择 6.进行设置目标位置,配置分区 7.选择网络和主机名 8.配置…...
Kafka 重平衡
Kafka 重平衡协调者RebalanceRebalance 条件Rebalance 避免Rebalance : 让单 Group 下所有的 Consumer 怎么消费订阅主题的所有分区Rebalance 时 , 所有 Consumer 要共同参与 (无法消费),在协调者 (Coordinator) 协调下,完成订阅主题分区的分配 协调者…...
PTA:L1-022 奇偶分家、L1-023 输出GPLT、L1-024 后天(C++)
目录 L1-022 奇偶分家 问题描述: L1-023 输出GPLT 问题描述: 实现代码: L1-024 后天 问题描述: 实现代码: 简单题,没写题解,看代码就能看懂 L1-022 奇偶分家 问题描述: 给…...

IDEA插件开发入门.02
前言许久没更新IDEA插件开发系列了。最近刚好在汇总日常开发中常见的代码“异味”,共享文档复制黏贴略显麻烦,所以想着是否可以搞一个IDEA插件来帮忙收集常见代码,毕竟IDEA作为后端程序员必备的开发工具,显然会方便很多。于是&…...
如何用 23 种编程语言说“Hello World”
在编程的世界里," Hello World " 往往是开发者开始学习一种新语言时写的第一个程序。这个简单的程序会将 “Hello World“ 输出在我们的屏幕上。看似很简单的行为,实际上对于每一个新学习编程语言的人来说,它代表着新的起点。那么&…...

【Linux快速入门】文件目录操作
文章目录概念1. Linux文件系统概述2. Linux文件目录结构3. Linux文件和目录操作3.1 文件操作3.1.1 创建文件3.1.2 复制文件3.1.3 移动文件3.1.4 删除文件3.1.5 查看文件3.1.6 输出指令3.1.7 >和>>指令3.2 目录操作3.2.1 创建目录3.2.2 复制目录3.2.3 移动目录3.2.4 删…...

字体反爬慢慢总结破解方式
什么是字体反爬 网页开发者自己创造一种字体,因为在字体中每个汉字都有其代号,那么以后再网页中不会直接显示这个文字的效果。而是显示其代号,因此即使获取了网页的文本内容。也只是获取到文字的代号,而不是文字本身。 简单来说&…...
Kafka 位移提交
Kafka 位移提交自动提交手动提交Consumer 的消费位移 : 记录 Consumer 下一条消息的消费位移 如 : Consumer 已消费 5 条消息 (位移: 0 - 4) , 此时 Consumer 位移 5 : 指向下一条消息的位移 提交位移 (Committing Offsets) : Consumer 向 Kafka 汇报位移数据 Consumer 能同…...

kubernetes--监控容器运行时:Falco
目录 Falco介绍 Falco架构 Falco的安装 告警规则示列 威胁场景测试: 监控容器创建的不可信任进程(自定义规则) Falco支持五种输出告警方式falco.yaml: Falco告警集中化展示: Falco介绍 Falco是一个Linux安全工具…...

HTTP协议详解(上)
目录 前言: 认识URL HTTP协议方法 通过Fiddler抓包 GET和POST之间典型区别 header详解 HTTP响应状态码 常见状态码解释 状态码分类 HTTP协议报文格式 小结: 前言: HTTP协议属于应用层协议,称为超文本传输协议ÿ…...
java性能-原生内存-内存分析
原生内存最佳实践 内存占用 jVM使用的原生内存和堆内存总和就是一个应用程序的总内存——操作系统角度 jvm启动时候加载的类路径下的jar文件相关的内存和系统其他进程共享资源的可能 测量内存占用 线程是个例外——每当创建一个线程操作系统都会分配一些原生内存存储线程栈…...

c++类与对象
🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章 🔥座右铭:“不要等到什么都没有了,才下定决心去做” …...

Java并发编程与API详解
文章目录前言操作系统——进程和线程进程进程组成进程状态进程控制进程创建进程终止进程阻塞和唤醒进程通信线程线程组成线程状态线程控制线程的实现方式用户线程内核线程混合方式CPU调度调度的层次调度的实现调度器调度的时机、切换与过程进程调度的方式闲逛进程两种线程的调度…...

【冲刺蓝桥杯的最后30天】day5
大家好😃,我是想要慢慢变得优秀的向阳🌞同学👨💻,断更了整整一年,又开始恢复CSDN更新,从今天开始更新备战蓝桥30天系列,一共30天,如果对你有帮助或者正在备…...

大厂与小厂招人的区别,看完多少有点不敢相信
前两天在头条发了一条招人的感慨,关于大厂招人和小公司招人的区别。 大厂:有影响力,有钱,能够吸引了大量的应聘者。因此,也就有了筛选的资格,比如必须985名校毕业,必须35岁以下,不能…...

前端ES5对象特性
ES5对象特性 对象和函数的原型 JS中每一个对象都有一个特殊的内置属性,这个特殊的对象可以指向其他的对象 我们通过引用对象的属性key来获取一个value时,它会触发 Get 的操作首先检查该对象是否有对应的属性,如果有的话就使用对象内的如果…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...
从零手写Java版本的LSM Tree (一):LSM Tree 概述
🔥 推荐一个高质量的Java LSM Tree开源项目! https://github.com/brianxiadong/java-lsm-tree java-lsm-tree 是一个从零实现的Log-Structured Merge Tree,专为高并发写入场景设计。 核心亮点: ⚡ 极致性能:写入速度超…...
Cursor AI 账号纯净度维护与高效注册指南
Cursor AI 账号纯净度维护与高效注册指南:解决限制问题的实战方案 风车无限免费邮箱系统网页端使用说明|快速获取邮箱|cursor|windsurf|augment 问题背景 在成功解决 Cursor 环境配置问题后,许多开发者仍面临账号纯净度不足导致的限制问题。无论使用 16…...

Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用
Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用 Linux 内核内存管理是构成整个内核性能和系统稳定性的基础,但这一子系统结构复杂,常常有设置失败、性能展示不良、OOM 杀进程等问题。要分析这些问题,需要一套工具化、…...