浅谈Python中的可变对象和不可变对象


Posted in Python onJuly 07, 2017

什么是可变/不可变对象

不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。

可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。

Python中,数值类型(int和float)、字符串str、元组tuple都是不可变类型。而列表list、字典dict、集合set是可变类型。

还是看代码比较直观。先看不可变对象

不可变对象的例子

先说明一点is 就是判断两个对象的id是否相同, 而 == 判断的则是内容是否相同。

a = 2
b = 2
c = a + 0 
c += 0

print(id(a), id(b), id(2)) # id都相同
print(c is b) #True

再来看字符串

astr = 'good'
bstr = 'good'
cstr = astr + ''
print(cstr is bstr) # True
print(id(astr), id(bstr), id('good')) # 三个id相同

和数值类型的结果一样。如果是下面这种情况,变量修改后不在是good

astr = 'good'
print(id(astr))
astr += 'aa'
print(id(astr)) # id和上面的不一样

由于是不可变对象,变量对应内存的值不允许被改变。当变量要改变时,实际上是把原来的值复制一份后再改变,开辟一个新的地址,astr再指向这个新的地址(所以前后astr的id不一样),原来astr对应的值因为不再有对象指向它,就会被垃圾回收。这对于int和float类型也是一样的。

再看tuple

add = (1, 2, 3)
aee = (1, 2, 3)
print(id(add), id(aee), id((1, 2, 3))) # id各不相同

aee = (1, 2, 3)
print(id(aee))
aee += () # 加空元组
print(id(aee)) # id变了!
print(aee) #(1 ,2,3)

虽然看上去都是(1 ,2, 3)按理说应该和上面一致才对。难道这是可变对象?再看

add = (1, 2, 3)
aee = add 
print(id(aee), id(add)) # 这两个id一样
aee += (4, 5, 6)
print(id(aee)) # aee的id变了!
print(add) # add还是(1, 2, 3)没有变

又和数值类型于str类型一致了。如果是可变对象add = aee,它们指向同一地址(id相同)是肯定的。但不是同一对象的不同引用,因为如果是的话,aee的改变会引起add的改变,再tuple中并不是这样。所以tuple是不可变对象,但又和str和数值类型稍微有点区别。平常说的tuple不可变更多时候是指里面存放的值不能被改变(有些特殊情况,如tuple里面存放了list,可改变list里的元素。但实际上这个tuple并没有被改变)。

对于str、int、float只要在它们再类型相同的情况下,值也相同,那么它们的id相同。(为什么要说类型相同?)

a = 2.0
b = 2
print(a is b) # False, 一个int一个float,类型都不同

2和2.0就不在一个地址上。

可变对象的例子

lis = [1, 2, 3]
lis2 = [1, 2, 3]
# 虽然它们的内容一样,但是它们指向的是不同的内存地址
print(lis is lis2)
print(id(lis), id(lis2), id([1, 2, 3])) # 三个id都不同

再看赋值的情况下

alist = [1, 2, 3]
# alist实际上是对对象的引用,blist = alist即引用的传递,现在两个引用都指向了同一个对象(地址)
blist = alist
print(id(alist), id(blist)) # id一样
# 所以其中一个变化,会影响到另外一个
blist.append(4)
print(alist) # 改变blist, alist也变成了[1 ,2 ,3 4]
print(id(alist), id(blist)) # id一样,和上面值没有改变时候的id也一样

blist = alist这一句。alist实际上是对对象的引用,blist = alist即引用的传递,现在两个引用都指向了同一个对象(地址)。所以其中一个变化,会影响到另外一个

再看看set

abb = {1, 2, 3}
acc = abb
print(id(abb), id(acc))
acc.add(4)
print(abb) # {1, 2, 3, 4} 
print(id(abb), id(acc)) # 相等

和上面list的例子一致。

可变对象由于所指对象可以被修改,所以无需复制一份之后再改变,直接原地改变,所以不会开辟新的内存,改变前后id不变。

当然不可变对象就不是这样了, 可以和这个对比一下

abc = 3
dd = abc
dd = 43
print(abc) # 3,并不随dd的改变而改变

但是如果是拷贝,就仅仅是将内容拷贝过去,传递的并是不引用。这在想使用列表的值又不想修改原列表的时候特别有用。

blist = alist[:] # or alist.copy()
print(alist is blist) # False
blist.append(4)
print(alist) # 还是[1,2 ,3]没有变化

作为函数参数

作为函数参数,也是一样的,可变类型传递的是引用,不可变类型传递的是内容。

test_list = [1, 2, 3, 4]
test_str = 'HAHA'


def change(alist):
  alist.append(5)


def not_change(astr):
  astr.lower()


change(test_list)
not_change(test_str)
print(test_list) # 改变了原来的值
print(test_str) # 没有变

当然了,如果不想改变原来列表的值,参数可以传入列变的拷贝。alsit[:]

有趣的例子

再看一个有趣的例子,我们知道list是可以使用+添加一个列表的。

a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2))
# 实际上是a2指向了新的对象,id已经改变。
# 所以现在a2、a1并不是同一对象的两个引用了,a2变化a1不会改变
a2 = a2 + [4] # 这个等式中,右边的a2还是和a1的id一样的,一旦赋值成功,a2就指向新的对象
print(id(1), id(a2)) # 不等,a2的id变化了
print(a1) # [1, 2, 3]没有变

如果是这样写

a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2))
a2 += [4] # 相当于调用了a2.extend([4]),原地改变并没有新的对象产生
print(id(1), id(a2)) # 相等,a2的id没有变化
print(a1)

不同的地方在于a2 += [4],这句相当于调用了a2.extend([4])相当于原地改变,并没有新的对象产生。

以上这篇浅谈Python中的可变对象和不可变对象就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
在Python的列表中利用remove()方法删除元素的教程
May 21 Python
Pytorch入门之mnist分类实例
Apr 14 Python
如何通过雪花算法用Python实现一个简单的发号器
Jul 03 Python
利用Python实现Shp格式向GeoJSON的转换方法
Jul 09 Python
python实现连连看辅助之图像识别延伸
Jul 17 Python
使用django的objects.filter()方法匹配多个关键字的方法
Jul 18 Python
python爬虫项目设置一个中断重连的程序的实现
Jul 26 Python
解决Python3.8用pip安装turtle-0.0.2出现错误问题
Feb 11 Python
jupyter notebook 多行输出实例
Apr 09 Python
python根据用户需求输入想爬取的内容及页数爬取图片方法详解
Aug 03 Python
Python Http请求json解析库用法解析
Nov 28 Python
PyCharm Ctrl+Shift+F 失灵的简单有效解决操作
Jan 15 Python
Python MySQL数据库连接池组件pymysqlpool详解
Jul 07 #Python
Python用Pillow(PIL)进行简单的图像操作方法
Jul 07 #Python
python 表达式和语句及for、while循环练习实例
Jul 07 #Python
Python中字典和集合学习小结
Jul 07 #Python
使用python实现接口的方法
Jul 07 #Python
python函数中return后的语句一定不会执行吗?
Jul 06 #Python
不可错过的十本Python好书
Jul 06 #Python
You might like
PHP无敌近乎加密方式!
2010/07/17 PHP
php自定义函数实现二维数组排序功能
2016/07/20 PHP
如何实现iframe(嵌入式帧)的自适应高度
2006/07/26 Javascript
setInterval 和 setTimeout会产生内存溢出
2008/02/15 Javascript
JavaScript.The.Good.Parts阅读笔记(二)作用域&闭包&减缓全局空间污染
2010/11/16 Javascript
使用JS或jQuery模拟鼠标点击a标签事件代码
2014/03/10 Javascript
js获取下拉列表的值和元素个数示例
2014/05/07 Javascript
jQuery的animate函数学习记录
2014/08/08 Javascript
Bootstrap所支持的表单控件实例详解
2016/05/16 Javascript
如何在JS中实现相互转换XML和JSON
2016/07/19 Javascript
Bootstrap基本组件学习笔记之分页(12)
2016/12/08 Javascript
windows下vue.js开发环境搭建教程
2017/03/20 Javascript
微信小程序实现移动端滑动分页效果(ajax)
2017/06/13 Javascript
浅谈webpack对样式的处理
2018/01/05 Javascript
jQuery实现表格的增、删、改操作示例
2019/01/27 jQuery
JavaScript多种滤镜算法实现代码实例
2019/12/10 Javascript
vue 中的 render 函数作用详解
2020/02/28 Javascript
[41:21]夜魇凡尔赛茶话会 第三期02:看图识人
2021/03/11 DOTA
Python读取图片EXIF信息类库介绍和使用实例
2014/07/10 Python
python将文本转换成图片输出的方法
2015/04/28 Python
在Windows系统上搭建Nginx+Python+MySQL环境的教程
2015/12/25 Python
Python实现拷贝多个文件到同一目录的方法
2016/09/19 Python
理解python中生成器用法
2017/12/20 Python
python Crypto模块的安装与使用方法
2017/12/21 Python
Python 实现在文件中的每一行添加一个逗号
2018/04/29 Python
查看TensorFlow checkpoint文件中的变量名和对应值方法
2018/06/14 Python
python批量合成bilibili的m4s缓存文件为MP4格式 ver2.5
2020/12/01 Python
Python 排序最长英文单词链(列表中前一个单词末字母是下一个单词的首字母)
2020/12/14 Python
Infababy英国:婴儿推车、Travel System婴儿车和婴儿汽车座椅销售
2018/05/23 全球购物
Ariat官网:美国马靴和服装品牌
2019/12/16 全球购物
函授本科自我鉴定
2013/11/03 职场文书
市政工程技术专业自荐书
2014/07/06 职场文书
小学安全教育月活动总结
2014/07/07 职场文书
2015大学生暑假调查报告
2015/07/13 职场文书
教师节感想
2015/08/11 职场文书
详解Python 3.10 中的新功能和变化
2021/04/28 Python