Python可变与不可变数据和深拷贝与浅拷贝


Posted in Python onApril 06, 2022

浅拷贝和深拷贝

拷贝函数是专门为可变数据类型listsetdict使用的一种函数。作用是,当一个值指向另一个值的时候,也不会影响指向的值,如果被指向的数据是可变数据,那么它一旦被修改,指向的数据也会随之改变。

什么是可变数据和不可变数据

我们来举一个例子,整型是不可变的数据,那么为什么是不可变的数据呢?一个数据是不是可变的就要关系到python的缓存机制。

当一个数据发生变化,如果它的内存地址没有发生变化,就说明这是一个可变数据。

比如说,我们现在创建一个值是a的变量,它的值是100,然后让这个数值发生变化,观察者个变量的内存地址是否发生了变化。

a = 100
print(a, id(a)) # 100 1610845392

a += 100
print(a, id(a)) # 200 1610848592

我们发现数值发生了变化,变量的内存也跟着发生了变化,我们再创建一个变量b,值也是整型100

b = 100
print(b, id(b))	# 100 1610845392

发现b的内存地址和a的内存地址是一样的,也就是说,像整型这样的数据类型,一个数字就独占一个内存地址,当某个指向这个值的变量,发生了变化的时候,不是这个变量的值要改变,而是这个变量要寻找改变后的值的内存地址,然后重新的指向它。只要你的硬件不重新启动,那么这个内存地址就永远也不会发生变化了,这样的数据就是不可变数据。

那么,反之就是可变数据,指的就是当变量指向的值发生变化之后,在这个内存地址上的值实打实的发生变化的值,就是可变数据类型。

比如列表,列表发生改变之后,是在原有的基础上发生变化的,所以内存地址是不会改变的,这就是可变数据类型,可变数据类型没有内存缓存机制,不能节省内存,所以一模一样的数据,他们的内存地址可能是不相同的。

a = [1, 2]
print(a, id(a)) # [1, 2] 1528536069896

a.append(3)
print(a, id(a)) # [1, 2, 3] 1528536069896

# b 和 a的值相同,但是内存地址不相同
b = [1, 2, 3]
print(b, id(b)) # [1, 2, 3] 1528536069832

那么拷贝函数是干什么的?

在我们的实际工作当中,经常会使用的一种操作就是定义一个变量,它的值直接就赋给了一个原有的变量之上。可是变量定义之后我们绝不是用来作为一个摆设的,而是要做运算、或者是做一个临时的存储,那么原有的变量的值是要改变的,问题就来了,如果是一个不可变的数据还好,如果是可变的数据,直接的赋值他们的内存地址是相同的, 如果一个变量的值发生变化,同内存地址的的值就都发生改变了,我们的向要临时存储的值也就不再是我们想要的那个值了,这是绝大多数的时候我们不想看到的结果。

我们拿整型为例,变量a直接赋值给变量b,这个时候的变量a b 的值是相同的,但是如果变量a的值发生了变化,是丝毫不影响变量b的值的。

a = 100
print(a, id(a))  # 100 1610845392

b = a
print(b, id(b))  # 100 1610845392

a += 100
print(a, id(a))  # 200 1610848592
print(b, id(b))  # 100 1610845392

但是如果是可变数据就不是这样的情况了

a = [1, 2]
print(a, id(a))  # [1, 2] 2077688035080

b = a
print(b, id(b))  # [1, 2] 2077688035080

a.append(3)
print(a, id(a))  # [1, 2, 3] 2077688035080
print(b, id(b))  # [1, 2, 3] 2077688035080

不可变数据的这个特性既是一个优点也是一个缺点,缺点就是如果我们想要保存a变量发生变化之前的的一个状况的时候,是保存不下来的,这个时候就出现了拷贝函数,它可以将可变数据变成不可变数据那样的效果。

浅拷贝

使用拷贝函数,将a变量放入作为参数放入函数中,使用b变量接受函数的返回值,就成功的拷贝了变量a,变量b的内存地址和变量a的不一样,这样当它们其中一方发生变化之后,不会影响到另一方的数据。

# 拷贝函数不能直接使用,需要使用import导入copy模块,copy模块的copy函数就是浅拷贝

import copy

a = [1, 2, 3]

# 变量b不在直接是变量a的直接赋值了,而是通过copy函数的返回值
b = copy.copy(a)

# 他们的数值一样,但是内存地址不同,所以他们之间的任意一方发生变化都不会影响到第二方。
print(a, id(a))  # [1, 2, 3] 2343743813320
print(b, id(b))  # [1, 2, 3] 2343743813192

a.append(4)
print(a, id(a))  # [1, 2, 3, 4] 2343743813320
print(b, id(b))  # [1, 2, 3] 2343743813192

但是如果变量a是一个二级容器或者是一个更多级容器,浅拷贝无法拷贝第二级容器或者更多级的容器,所以当第二级容器或者是更多级的容器发生变化的时候,还是会发生变化,因为浅拷贝只能拷贝一级容器,所以多级容器的内存地址还是相同的。

import copy

a = [[66,88], 2, 3]

b = copy.copy(a)

print(a, id(a))  # [[66, 88], 2, 3] 2431683163720
print(b, id(b))  # [[66, 88], 2, 3] 2431683162184

# 改变二级容器
a[0].append(100)
print(a, id(a))  # [[66, 88, 100], 2, 3] 2431683163720
print(b, id(b))  # [[66, 88, 100], 2, 3] 2431683162184

# 浅拷贝不能拷贝二级及以上的容器
print(id(a[0]))  # 1582481372872
print(id(b[0]))  # 1582481372872

深拷贝

浅拷贝只能拷贝一级容器

所以诞生了深拷贝,深拷贝可以拷贝所有级别的容器。

import copy

a = [[66,88], 2, 3]

# 深拷贝使用deepcopy函数
b = copy.deepcopy(a)


print(a, id(a))  # [[66, 88], 2, 3] 2168411158088
print(b, id(b))  # [[66, 88], 2, 3] 2168411156552

a[0].append(100)
print(a, id(a))  # [[66, 88, 100], 2, 3] 2168411158088
print(b, id(b))  # [[66, 88], 2, 3] 2168411156552

# 深拷贝所有级别的容器
print(id(a[0]))  # 2168411158216
print(id(b[0]))  # 2168411122760

总结

使用深浅拷贝需要导入copy模块;

浅拷贝使用copy函数,只能拷贝一级容器的所有元素;

深拷贝使用deepcopy函数,可以拷贝所有级别容器的所有元素;

标准库copy中只有copydeepcopy两个函数对外开放使用;

因为深拷贝要拷贝的元素跟多,所以速度会远不如浅拷贝,在编程的过程中要注意避免造成多余的系统负担;

python中的不可变数据是Number、string、tuple,可变数据是list、set、dict;而拷贝就是专门为可变数据提供的,所以深浅拷贝只适用于list、set、dict,当然,可变数据使用拷贝函数也不会出错,但是没有意义。

到此这篇关于Python可变与不可变数据和深拷贝与浅拷贝的文章就介绍到这了,更多相关Python数据与拷贝内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python从MP3文件获取id3的方法
Jun 15 Python
python开发中module模块用法实例分析
Nov 12 Python
简单掌握Python中glob模块查找文件路径的用法
Jul 05 Python
Python使用pymysql小技巧
Jun 04 Python
Python中关于Sequence切片的下标问题详解
Jun 15 Python
浅谈python装饰器探究与参数的领取
Dec 01 Python
python如何实现一个刷网页小程序
Nov 27 Python
Python代码太长换行的实现
Jul 05 Python
Python re 模块findall() 函数返回值展现方式解析
Aug 09 Python
Python3 tkinter 实现文件读取及保存功能
Sep 12 Python
PyCharm中如何直接使用Anaconda已安装的库
May 28 Python
关于python3.9安装wordcloud出错的问题及解决办法
Nov 02 Python
Python 全局空间和局部空间
Apr 06 #Python
Selenium浏览器自动化如何上传文件
Apr 06 #Python
在Python 中将类对象序列化为JSON
Apr 06 #Python
Python中itertools库的四个函数介绍
Apr 06 #Python
如何用六步教会你使用python爬虫爬取数据
基于Python实现射击小游戏的制作
python使用opencv对图像添加噪声(高斯/椒盐/泊松/斑点)
You might like
PHP 检查扩展库或函数是否可用的代码
2010/04/06 PHP
php操作excel文件 基于phpexcel
2010/07/02 PHP
Windows下利用Gvim写PHP产生中文乱码问题解决方法
2011/04/20 PHP
PHP读取文件内容后清空文件示例代码
2014/03/18 PHP
php+curl 发送图片处理代码分享
2015/07/09 PHP
zen_cart实现支付前生成订单的方法
2016/05/06 PHP
php查询操作实现投票功能
2016/05/09 PHP
PHP实现按之字形顺序打印二叉树的方法
2018/01/16 PHP
Centos7安装swoole扩展操作示例
2020/03/26 PHP
兼容IE和Firefox火狐的上下、左右循环无间断滚动JS代码
2013/04/19 Javascript
js获取php变量的实现代码
2013/08/10 Javascript
javascript异步编程代码书写规范Promise学习笔记
2015/02/11 Javascript
基于JavaScript实现智能右键菜单
2016/03/02 Javascript
JavaScript设计模式开发中组合模式的使用教程
2016/05/18 Javascript
JS对象是否拥有某属性如何判断
2017/02/03 Javascript
分享vue.js devtools遇到一系列问题
2017/10/24 Javascript
JavaScript门道之标准库
2018/05/26 Javascript
使用Vue-cli 3.0搭建Vue项目的方法
2018/06/07 Javascript
OpenLayer3自定义测量控件MeasureTool
2020/09/28 Javascript
Python实现向服务器请求压缩数据及解压缩数据的方法示例
2017/06/09 Python
对Python w和w+权限的区别详解
2019/01/23 Python
django 信号调度机制详解
2019/07/19 Python
python 多进程并行编程 ProcessPoolExecutor的实现
2019/10/11 Python
Python基础之列表常见操作经典实例详解
2020/02/26 Python
appium+python adb常用命令分享
2020/03/06 Python
解决keras GAN训练是loss不发生变化,accuracy一直为0.5的问题
2020/07/02 Python
HTML5 贪吃蛇游戏实现思路及源代码
2013/09/03 HTML / CSS
Omio中国:全欧洲低价大巴、火车和航班搜索和比价
2018/08/09 全球购物
办理护照介绍信
2014/01/16 职场文书
英文演讲稿
2014/05/15 职场文书
令人印象深刻的自荐信
2014/05/25 职场文书
新农村建设典型材料
2014/05/31 职场文书
学位证书委托书
2014/09/30 职场文书
2015年基层党建工作汇报材料
2015/06/25 职场文书
党章党规党纪学习心得体会
2016/01/14 职场文书
入团申请书格式
2019/06/20 职场文书