Python中可变和不可变对象的深入讲解


Posted in Python onAugust 02, 2021

前置知识

在 Python 中,一切皆为对象

Python 中不存在值传递,一切传递的都是对象的引用,也可以认为是传址 

有哪些可变对象,哪些不可变对象?

不可变对象:字符串、元组、数字(int、float)

可变对象:数组、字典、集合 

不可变对象和可变对象的区别?

可变对象:改变对象内容,对象在内存中的地址不会被改变

不可变对象:改变对象内容,对象在内存中的地址会被改变;如果必须存储一个不同的值,则必须创建新的对象 

不可变对象的应用场景

它们在需要常量哈希值的地方起着重要作用,例如作为字典中的键

从内存角度出发说下有什么区别?

不可变对象

Python中可变和不可变对象的深入讲解

Python 中的变量有一个内存空间

具体的数据(对象)也有一个内存空间

而变量保存(指向)的是存储数据(对象)的内存地址,一般也叫对象引用

不可变对象是指对象内容本身不可变

变的是:改变了值,会创建新对象,然后变量改变了对象引用,指向了新对象,旧对象会被垃圾回收

可变对象

Python中可变和不可变对象的深入讲解

变的是:原来对象的内容,不会创建新对象,而变量也还是指向原对象

从代码角度看看区别

不可变对象-整型

a = 123
b = a
print(id(a))
print(id(b))
print(a, b)

a += 2

print(id(a))
print(id(b))
print(a, b)


# 输出结果
4473956912
4473956912
123 123
4473956976
4473956912
125 123

从前两次打印可以看到,a、b 变量保存的内存地址是同一个,他们们都保存了 123 的内存地址(123 对象的引用)

预期情况:在 a 做了加法赋值运算之后,既然他们一开始都是指向同一个内存地址,按道理修改 123 后,他们也应该仍然指向同一个内存地址呀,但是并没有!

实际情况:a 指向了新的内存地址,而 b 仍然指向旧的内存地址,所以他们的值也不一样 

可以看看下面的图

首先,这是一个内存区域

Python中可变和不可变对象的深入讲解

原理

因为数字(int、float) 是不可变对象,所以不能在 123 的内存地址上直接修改数据

加法赋值,实际上是将原来的 123 复制了一份到新的内存地址,然后再做加法,得到一个新的值 125,最后 a 再指向新的内存地址

不可变对象-字符串

a = "test"
b = a
print(id(a))
print(id(b))
print(a, b)

a += "123"

print(id(a))
print(id(b))
print(a, b)


# 输出结果
4455345392
4455345392
test test
4455818288
4455345392
test123 test

不可变对象-元组

a = (1, 2, 3)
b = a
print(id(a))
print(id(b))
print(a, b)

a = a + a
print(id(a))
print(id(b))
print(a, b)


# 输出结果
4455410240
4455410240
(1, 2, 3) (1, 2, 3)
4455359200
4455410240
(1, 2, 3, 1, 2, 3) (1, 2, 3)

可变对象列表

# 列表
a = [1, 2, 3]
b = a

print(id(a))
print(id(b))
print(a, b)

a += [4, 5, 6]

print(a, b)
print(id(a))
print(id(b))


# 输出结果
4327665856
4327665856
[1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6]
4327665856
4327665856

能看到 a 变量修改值之后,b 的值也随之修改了

可以看看下面的图

Python中可变和不可变对象的深入讲解 

因为 list 是不可变对象,所以并不会将原来的值复制到新的内存地址再改变,而是直接在原来的内存地址上修改数据
因为 a、b 都是指向原来的内存地址的,所以 a、b 变量保存的内存地址是一致的(对象引用是一致的),当然值也是一样的啦 

Python 函数的参数传递

这里先提前讲下函数的入门,因为参数传递是个挺重要的点

概念

开头有讲到,Python 的一切传递都是对象的引用,函数参数传递也不例外

当传递给函数的是一个变量,实际上传递的是变量保存的对象引用(变量指向的内存地址)

在函数内部修改变量时,会根据变量指向的内存地址,去修改对应的值才对,事实真是如此吗

参数传递不可变对象

# 函数
def test_no_define(age, name):
    age = 123
    name = "poloyy"
    print(age, name)


age = 1
name = "yy"
print(age, name)

test_no_define(age, name)
print(age, name)


# 输出结果
1 yy
123 poloyy
1 yy

参数传递可变对象

# 函数
def test_define(dicts, sets):
    dicts['age'] = 24
    sets.pop()
    print(dicts, sets)


dicts = {"age": 123}
sets = {1, 2}
print(dicts, sets)

test_define(dicts, sets)
print(dicts, sets)


# 输出结果
1 yy
{'age': 123} {1, 2}
{'age': 24} {2}
{'age': 24} {2}

总结

当函数参数传递的变量是不可变对象的时候,函数内改变变量值,函数外的变量不会随之改变

当函数参数传递的变量是可变对象的时候,函数内改变变量值,函数外的变量会随之改变

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

Python 相关文章推荐
python模拟登陆Tom邮箱示例分享
Jan 13 Python
解读Python编程中的命名空间与作用域
Oct 16 Python
Python数字图像处理之霍夫线变换实现详解
Jan 12 Python
利用python打开摄像头及颜色检测方法
Aug 03 Python
Python cv2 图像自适应灰度直方图均衡化处理方法
Dec 07 Python
Python3.4学习笔记之列表、数组操作示例
Mar 01 Python
python3中的eval和exec的区别与联系
Oct 10 Python
python列表切片和嵌套列表取值操作详解
Feb 27 Python
Python利用for循环打印星号三角形的案例
Apr 12 Python
Python3 ffmpeg视频转换工具使用方法解析
Aug 10 Python
Python用K-means聚类算法进行客户分群的实现
Aug 23 Python
Python3中对json格式数据的分析处理
Jan 28 Python
Python基础数据类型tuple元组的概念与用法
Aug 02 #Python
opencv用VS2013调试时用Image Watch插件查看图片
基于python定位棋子位置及识别棋子颜色
Python 处理表格进行成绩排序的操作代码
python识别围棋定位棋盘位置
python之基数排序的实现
Jul 26 #Python
python之PySide2安装使用及QT Designer UI设计案例教程
You might like
php header函数的常用http头设置
2015/06/25 PHP
基于Laravel实现的用户动态模块开发
2017/09/21 PHP
ThinkPHP开发--使用七牛云储存
2017/09/14 PHP
Javascript面向对象编程(二) 构造函数的继承
2011/08/28 Javascript
Webkit的跨域安全问题说明
2011/09/13 Javascript
不同的jQuery API来处理不同的浏览器事件
2012/12/09 Javascript
javascript动态添加、修改、删除对象的属性与方法详解
2014/01/27 Javascript
jquery通过visible来判断标签是否显示或隐藏
2014/05/08 Javascript
深入理解JavaScript系列(35):设计模式之迭代器模式详解
2015/03/03 Javascript
iscroll碰到Select无法选择下拉刷新的解决办法
2016/05/21 Javascript
JS实现探测网站链接的方法【测试可用】
2016/11/08 Javascript
jquery中绑定事件的异同
2017/02/28 Javascript
教你快速搭建Node.Js服务器的方法教程
2017/03/30 Javascript
微信小程序页面滑动屏幕加载数据效果
2020/11/16 Javascript
javascript基础进阶_深入剖析执行环境及作用域链
2017/09/05 Javascript
JS实现基于拖拽改变物体大小的方法
2018/01/23 Javascript
使用JavaScript生成罗马字符的实例代码
2018/06/08 Javascript
详解JavaScript中操作符和表达式
2018/09/12 Javascript
vue vant Area组件使用详解
2019/12/09 Javascript
es6数组之扩展运算符操作实例分析
2020/04/25 Javascript
[47:04]LGD vs infamous Supermajor小组赛D组 BO3 第二场 6.3
2018/06/04 DOTA
在Windows系统上搭建Nginx+Python+MySQL环境的教程
2015/12/25 Python
Python+matplotlib实现计算两个信号的交叉谱密度实例
2018/01/08 Python
Python timer定时器两种常用方法解析
2020/01/20 Python
使用Django实现把两个模型类的数据聚合在一起
2020/03/28 Python
matplotlib绘制鼠标的十字光标的实现(自定义方式,官方实例)
2021/01/10 Python
利用HTML5 Canvas制作键盘及鼠标动画的实例分享
2016/03/15 HTML / CSS
Joules美国官网:出色的英国风格
2017/10/30 全球购物
新奥尔良珠宝:Mignon Faget
2020/11/23 全球购物
捷克多品牌在线时尚商店:ANSWEAR.cz
2020/10/03 全球购物
门卫岗位职责
2013/11/15 职场文书
大学生职业生涯规划书范文
2014/01/04 职场文书
优秀护士演讲稿
2014/04/30 职场文书
2014年高三班主任工作总结
2014/12/05 职场文书
创业计划书之奶茶店开店方案范本!
2019/08/06 职场文书
Python内置数据类型中的集合详解
2022/03/18 Python