【Python】赋值、浅拷贝与深拷贝(附图解)

2023-11-18

Python的变量

先从Python的变量讲起,Python的变量比较特殊,变量本身不占空间,而是变量指向的内存地址占空间。变量名可以理解为该内存空间的别名,与C语言的引用类似。也可以更通俗地理解为:内存空间是一个瓶子,而变量名是瓶子的标签。

print(id(5))
a = 5
print(id(a))

结果如下:

140704116623136
140704116623136

可变对象与不可变对象

有句话是这么说的:“Python万物皆对象”,不管是数字1还是字符串"Hello world",又或者是更复杂的列表,他们都是对象。而对象按照其是否可变又分为可变对象与不可变对象。

可变对象

可变对象就是对象可以在内存地址不变的情况下改变数值。换句话说,可以在不创建新对象的情况下改变对象的值。

a = [1, 2, 3]
print("a =", a)
print("id =", id(a))
a.append(4)
print("a =", a)
print("id =", id(a))

结果如下:

a = [1, 2, 3]
id = 2595801473344
a = [1, 2, 3, 4]
id = 2595801473344

example

可以看到,在列表a添加了子元素4之后,其值发生改变,但是其内存地址并没有发生改变,或者说,没有创建新对象。

Python中常见的可变对象有:

  • 列表list
  • 集合set
  • 字典dict

不可变对象

与可变对象相对应,不可变对象就是必须创建新对象才能改变其值。

a = 1
print("a=", a)
print("id =", id(a))
a += 1
print("a=", a)
print("id =", id(a))

结果如下:

a = 1
id = 140704172066464
a = 2
id = 140704172066496

example

可以看到当a的数值从1增加到2后,a的id值也发生了变化,而本质上其实是a创建了新对象,该过程等价于:

a = int(1)
print("a=", a)
print("id =", id(a))
a = int(2)
print("a=", a)
print("id =", id(a))

Python中常见的不可变对象有:

  • 整型int
  • 字符串str
  • 元组tuple

赋值、浅拷贝与深拷贝的区别

可变对象的赋值、浅拷贝与深拷贝

赋值

赋值只是简单的引用。之前我们说过,Python变量的变量名可以理解为该内存地址的别名,而将一个变量赋值给另一个变量,其本质也相同。我们以一个例子说明:

a = [1, 2, 3]
b = a
print("a =", a)
print("b =", b)
print("a.id =", id(a))
print("b.id =", id(b))
a.append(4)
print("-"*20)
print("a =", a)
print("b =", b)
print("a.id =", id(a))
print("b.id =", id(b))

结果如下:

a = [1, 2, 3]
b = [1, 2, 3]
a.id = 1565564084608
b.id = 1565564084608
--------------------
a = [1, 2, 3, 4]
b = [1, 2, 3, 4]
a.id = 1565564084608
b.id = 1565564084608

可以看到,通过赋值的方式,a和b有一样的id值,指向的列表是一样的,所以一直保持b = a

example

浅拷贝

我们直接看例子:

from copy import copy
a = [1, 2, 3, [1, 2, 3]]
b = copy(a)
print("a =", a)
print("b =", b)
print("a.id =", id(a))
print("b.id =", id(b))
print("a[3].id =", id(a[3]))
print("b[3].id =", id(b[3]))
print("-"*25)
a.append(4)
a[3].append(4)
print("a =", a)
print("b =", b)
print("a.id =", id(a))
print("b.id =", id(b))
print("a[3].id =", id(a[3]))
print("b[3].id =", id(b[3]))

结果如下:

a = [1, 2, 3, [1, 2, 3]]
b = [1, 2, 3, [1, 2, 3]]
a.id = 2753416727552
b.id = 2753417852608
a[3].id = 2753416742336
b[3].id = 2753416742336
-------------------------
a = [1, 2, 3, [1, 2, 3, 4], 4]
b = [1, 2, 3, [1, 2, 3, 4]]
a.id = 2753416727552
b.id = 2753417852608
a[3].id = 2753416742336
b[3].id = 2753416742336

从结果看,经过浅拷贝后,列表a和列表b的id值(内存地址)不同,表示ab是两个不同的列表对象,因此当a添加新元素时,b的值不会发生变化。但是,a[3] = [1, 2, 3]是列表,前面说过列表是可变对象,而a[3]b[3]的id值是一样的,因此当a[3]对象发生变化时(对象内的增删改,不创建新对象),b[3]也对应发生改变。

example

深拷贝

直接看例子:
注意:这个例子和上面浅拷贝的一样,只是把copy换成deepcopy

from copy import deepcopy
a = [1, 2, 3, [1, 2, 3]]
b = deepcopy(a)
print("a =", a)
print("b =", b)
print("a.id =", id(a))
print("b.id =", id(b))
print("a[3].id =", id(a[3]))
print("b[3].id =", id(b[3]))
print("-"*25)
a.append(4)
a[3].append(4)
print("a =", a)
print("b =", b)
print("a.id =", id(a))
print("b.id =", id(b))
print("a[3].id =", id(a[3]))
print("b[3].id =", id(b[3]))

结果如下:

a = [1, 2, 3, [1, 2, 3]]
b = [1, 2, 3, [1, 2, 3]]
a.id = 2259013929984
b.id = 2259015054976
a[3].id = 2259013944704
b[3].id = 2259015054272
-------------------------
a = [1, 2, 3, [1, 2, 3, 4], 4]
b = [1, 2, 3, [1, 2, 3]]
a.id = 2259013929984
b.id = 2259015054976
a[3].id = 2259013944704
b[3].id = 2259015054272

从结果来看,b完全不受a的影响。是的,这就是深拷贝,它相当于逐层的浅拷贝,不管是ab还是a[3]b[3],它们的id值(内存地址)都不相同,它们都是不同的对象,因此当一方发生变化时,另外一方不受影响。

example

不可变对象的赋值、浅拷贝与深拷贝

不可变对象是不可变的,发生变化时必然创建新对象,因此不管是赋值、浅拷贝还是深拷贝,当一方变化时,另外一方都不会受到影响。

from copy import copy, deepcopy
a = 1
b = a
c = copy(a)
d = deepcopy(a)
print("a =", a, "b =", b, "c =", c, "d =", d)
print("a.id =", id(a), "b.id =", id(b), "c.id =", id(c), "d.id =", id(d))
print("-"*25)
a += 1
print("a =", a, "b =", b, "c =", c, "d =", d)
print("a.id =", id(a), "b.id =", id(b), "c.id =", id(c), "d.id =", id(d))
a = 1 b = 1 c = 1 d = 1
a.id = 140704172066464 b.id = 140704172066464 c.id = 140704172066464 d.id = 140704172066464
-------------------------
a = 2 b = 1 c = 1 d = 1
a.id = 140704172066496 b.id = 140704172066464 c.id = 140704172066464 d.id = 140704172066464

结论

  • 对于可变对象(列表、字典、集合等)
    • 赋值(b = a):使b和a保持绝对的同步,当一方发生变化时(对象内的增删改,不创建新对象),另外一方也会发生同样的变化
    • 浅拷贝(c = copy(a)):会拷贝一层,即id(a) != id©。但是,当a的子元素也是可变对象时,该可变对象发生变化时(对象内的增删改,不创建新对象),c中对应的可变对象也会跟着一起变化
    • 深拷贝(d = deepcopy(a)):d和a是完全独立的两个对象,a发生任何变化都不会影响d
  • 对于不可变对象(整型、字符串、元组等)
    • 不管是赋值、浅拷贝还是深拷贝,当一方发生变化时,另外一方都完全不受影响

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Python】赋值、浅拷贝与深拷贝(附图解) 的相关文章

随机推荐

  • 使用ray对pytorch模型进行超参数调节

    def main num samples 10 max num epochs 10 gpus per trial 1 data dir os path abspath data load data data dir config l1 tu
  • gradle 编译 Spring 源码(亲测有效)

    天知道我今天下午经历了什么 从去年开始断断续续看源码 在 IDEA 中 Spring 源码只读不能写 所以每次都是将方法拷贝到 vscode 中 IDEA 中看代码逻辑 接着在 vscode 对应的方法上写注释 期初这种方式没觉得有什么不便
  • 【一】第一个java程序详解

    第一个java程序详解 一 前言 二 创建并编写java源代码的文件 创建java源代码文件 更改文件后缀 java代码的结构 三 编译执行 编译 执行 四 总结 五 附 java关键字 一 前言 通过之前上一节 开篇 Java语言介绍及环
  • 【java毕业设计】基于Spring Boot+mysql的网上商城购物系统设计与实现(程序源码)-网上商城购物系统

    基于Spring Boot mysql的网上商城购物系统设计与实现 程序源码 毕业论文 大家好 今天给大家介绍基于Spring Boot mysql的网上商城购物系统设计与实现 本论文只截取部分文章重点 文章末尾附有本毕业设计完整源码及论文
  • Spring的三级缓存解决循环依赖

    一 什么是Spring三级缓存 第一级缓存 也叫单例池 存放已经经历了完整生命周期的Bean对象 第二级缓存 存放早期暴露出来的Bean对象 实例化以后 就把对象放到这个Map中 Bean可能只经过实例化 属性还未填充 第三级缓存 存放早期
  • Leetcode 600. 不含连续1的非负整数 C++

    Leetcode 600 不含连续1的非负整数 题目 给定一个正整数 n 找出小于或等于 n 的非负整数中 其二进制表示不包含 连续的1 的个数 示例 输入 5 输出 5 解释 下面是带有相应二进制表示的非负整数 lt 5 0 0 1 1
  • cesium-API分类

    Viewer类 imageryLayers 影像 terrainProvider 地形 dataSources 矢量数据 entities 几何实体集合 Widgets 组件 初始化界面上的组件 Camera 相机 Event 事件 鼠标点
  • 蓝桥杯单片机组——程序框架及客观题

    文章目录 前言 程序框架 main 中断 两段式代码结构 单片机运行流程 代码风格 客观题 总结 目录 前言 前面两篇主要是介绍了蓝桥省赛的一些参赛技巧 此篇主要是分享程序框架和一些客观题的链接 程序框架 蓝桥的评分是综合了效果和代码步骤的
  • 30屏幕参数_荣耀30三大重要参数曝光:男女生都喜欢

    去年上半年 荣耀发布了年度旗舰手机荣耀20系列 作为荣耀的旗舰系列手机 每一代产品都是备受关注的 也是销量较为乐观的一个旗舰系列 每一代都有代言人 也体现了品牌方对于这个系列产品的自信 这一次的代言人是去年大火 并代言过荣耀多款产品的李现
  • 程序员的思维方式

    读书不觉已春深 一寸光阴一寸金 不觉间实习已近四周 我想起这首诗 并非标榜自己学习 工作有多投入 而是感慨时间静悄悄得溜过 只有当你回首时 才能觉察到它的存在且已过去 程序员浏览博客是必不可少的 但想要获得更多的成长 写博客是一种很好的方式
  • 2021数学建模竞赛国赛ABCD题目分析以及代码实现

    2021 年高教社杯全国大学生数学建模竞赛题目 请先阅读 全国大学生数学建模竞赛论文格式规范 C 题 生产企业原材料的订购与运输 某建筑和装饰板材的生产企业所用原材料主要是木质纤维和其他植物素纤维材料 总体可分为 A B C 三种类型 该企
  • Docker安装Elasticsearch

    Elasticsearch安装 ElasticSearch安装就相当于安装MySQL数据库 下载对应的镜像文件 docker pull elasticsearch 7 4 2 创建需要挂载的目录 mkdir p mydata elastic
  • ubuntu ARM下OpenCV的交叉编译

    OpenCV3 4 3编译安装 需求 环境及工具 利用cmake gui进行相关配置 1 cmake gui界面配置opencv 并生成Makefile 2 配置生成目标选项 3 配置编译器 4 指定编译内容 路径等配置 5 执行配置 生成
  • VS2015编译boost,并进行使用

    VS2015编译boost 并进行使用 1 到官网下载最新的boost www boost org 这里我下载的1 68版本 2 安装 解压后运行bootstrap bat文件 稍等一小会就OK 3 编译boost库 注意一定要使用VS20
  • 黑马jvm课程笔记d2

    目录 一 直接内存相关 1 1 定义 1 2 ByteBuffer 作用 1 3文件读写过程 1 3 直接内存溢出和释放 二 垃圾回收相关 2 1 判断是否可以回收 2 2 五种引用 编辑 2 2 1 软引用应用 2 2 2 弱引用应用 2
  • 【Linux-进程通信1】管道

    进程间通信介绍 进程间通信目的 在操作系统中 每个进程都是独立运行的 它们有自己的地址空间和资源 它们不能直接访问其他进程的资源 然而 在现代计算机系统中 很少有一个进程能够独立完成所有任务 因此需要不同的进程之间进行通信和协作 进程间通信
  • Linux中Ubuntu卡顿问题解决

    解决vmware虚拟机速度慢的3种方法 使用vmware虚拟操作系统的好处不用多说 但如果虚拟机运行速度十分缓慢 正常使用很卡的话是很痛苦的 本文介绍了3种提高虚拟机运行速度的方法 都是古意人实际操作过的方法 效果显著 推荐大家一试 1 给
  • 【复习】软考中级_软件设计师(2021)__上午

    前言 1 总想瞎bb点什么内容 2 自己复习的笔记 分享大家一起使用 3 可能有错误请指教 4 对于有些基础内容进行省略 5 适合等公交 摸鱼 无聊的时候观看 6 技术有限 哪里出错误了请指教 十分感谢 一 计算机组成与结构 数据的转换 1
  • 【C++ Primer 第五版】 目录-第Ⅰ部分

    序言和前言 第一章 开始 1 1 编解一个简单的C 程序 1 1 1 编译 运行程序 1 2 初识输入输出 1 3 注释简介 1 4 控制流 1 4 1 while 语句 1 4 2 for语句 1 4 3 读取数量不定的输入数据 1 4
  • 【Python】赋值、浅拷贝与深拷贝(附图解)

    Python 赋值 浅拷贝与深拷贝 附图解 Python的变量 可变对象与不可变对象 可变对象 不可变对象 赋值 浅拷贝与深拷贝的区别 可变对象的赋值 浅拷贝与深拷贝 赋值 浅拷贝 深拷贝 不可变对象的赋值 浅拷贝与深拷贝 结论 Pytho