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 的操作首先检查该对象是否有对应的属性,如果有的话就使用对象内的如果…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
