python新手学习可变和不可变对象


Posted in Python onJune 11, 2020

python中有可变对象和不可变对象,可变对象:list,dict.不可变对象有:int,string,float,tuple.

python不可变对象

int,string,float,tuple

先来看一个例子

def int_test(): 
 i = 77
 j = 77
 print(id(77))     #140396579590760
 print('i id:' + str(id(i)))  #i id:140396579590760
 print('j id:' + str(id(j)))  #j id:140396579590760
 print i is j     #True
 j = j + 1
 print('new i id:' + str(id(i))) #new i id:140396579590760
 print('new j id:' + str(id(j))) #new j id:140396579590736
 print i is j     #False
 
if __name__ == '__main__':
 int_test()

有i和j俩个变量的值为77,通过打印77的ID和变量i,j在内存中的id我们得知它们都是指向同一块内存。所以说i和j都是指向同一个对象的。然后我们修改j的值,让j的值+1.按道理j修改之后应该i的值也发生改变的,因为它们都是指向的同一块内存,但结果是并没有。因为int类型是不可变类型,所有其实是j复制了一份到新的内存地址然后+1,然后j又指向了新的地址。所以j的内存id发生了变化。

内存分配情况如下:

python新手学习可变和不可变对象

有i和j俩个变量的值为77,通过打印77的ID和变量i,j在内存中的id我们得知它们都是指向同一块内存。所以说i和j都是指向同一个对象的。然后我们修改j的值,让j的值+1.按道理j修改之后应该i的值也发生改变的,因为它们都是指向的同一块内存,但结果是并没有。因为int类型是不可变类型,所有其实是j复制了一份到新的内存地址然后+1,然后j又指向了新的地址。所以j的内存id发生了变化。

内存分配情况如下:

def dict_test():
 a = {}
 b = a
 print(id(a))
 a['a'] = 'hhhh'
 print('id a:' + str(id(a)))
 print('a:' + str(a))
 print('id b:' + str(id(b)))
 print('b:' + str(b))if __name__ == '__main__':
 dict_test()

运行结果如下:

140367329543360
id a:140367329543360
a:{'a': 'hhhh'}
id b:140367329543360
b:{'a': 'hhhh'}

可以看到a最早的内存地址id是140367329543360 然后把a赋值给b其实就是让变量b的也指向a所指向的内存空间。然后我们发现当a发生变化后,b也跟着发生变化了,因为list是可变类型,所以并不会复制一份再改变,而是直接在a所指向的内存空间修改数据,而b也是指向该内存空间的,自然b也就跟着改变了。

内存变化如下:

python新手学习可变和不可变对象

python函数的参数传递

由于python规定参数传递都是传递引用,也就是传递给函数的是原变量实际所指向的内存空间,修改的时候就会根据该引用的指向去修改该内存中的内容,所以按道理说我们在函数内改变了传递过来的参数的值的话,原来外部的变量也应该受到影响。

但是上面我们说到了python中有可变类型和不可变类型,这样的话,当传过来的是可变类型(list,dict)时,我们在函数内部修改就会影响函数外部的变量。而传入的是不可变类型时在函数内部修改改变量并不会影响函数外部的变量,因为修改的时候会先复制一份再修改。下面通过代码证明一下:

def test(a_int, b_list):
 a_int = a_int + 1
 b_list.append('13')
 print('inner a_int:' + str(a_int))
 print('inner b_list:' + str(b_list))
if __name__ == '__main__':
 a_int = 5
 b_list = [10, 11]
 test(a_int, b_list)
 print('outer a_int:' + str(a_int))
 print('outer b_list:' + str(b_list))

运行结果如下:

inner a_int:6
inner b_list:[10, 11, '13']
outer a_int:5
outer b_list:[10, 11, '13']

答案显而易见啦,经过test()方法修改后,传递过来的int类型外部变量没有发生改变,而list这种可变类型则因为test()方法的影响导致内容发生了改变。

总结:

在很多的其他语言中在传递参数的时候允许程序员选择值传递还是引用传递(比如c语言加上*号传递指针就是引用传递,而直接传递变量名就是值传递),而python只允许使用引用传递,但是它加上了可变类型和不可变类型,让我们感觉有点混乱了。听说python只允许引用传递是为方便内存管理,因为python使用的内存回收机制是计数器回收,就是每块内存上有一个计数器,表示当前有多少个对象指向该内存。每当一个变量不再使用时,就让该计数器-1,有新对象指向该内存时就让计数器+1,当计时器为0时,就可以收回这块内存了。

知识点扩展:

Python可变对象与不可变对象原理解析

原理

可变对象:list dict set

不可变对象:tuple string int float bool

1. python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象的引用,就不能直接修改原始对象——相当于通过“传值'来传递对象。

2. 当人们复制可变对象时,就复制了可变对象的引用,如果改变引用的值,则修改了原始的参数。

3. 为了简化内存管理,Python通过引用计数机制实现自动垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。

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

Python 相关文章推荐
在Python的Django框架下使用django-tagging的教程
May 30 Python
python select.select模块通信全过程解析
Sep 20 Python
Python+OpenCV实现图像融合的原理及代码
Dec 03 Python
python使用Qt界面以及逻辑实现方法
Jul 10 Python
Django Form and ModelForm的区别与使用
Dec 06 Python
已安装tensorflow-gpu,但keras无法使用GPU加速的解决
Feb 07 Python
使用 Python 在京东上抢口罩的思路详解
Feb 27 Python
xadmin使用formfield_for_dbfield函数过滤下拉表单实例
Apr 07 Python
使用Keras实现Tensor的相乘和相加代码
Jun 18 Python
python 发送get请求接口详解
Nov 17 Python
Python3+Flask安装使用教程详解
Feb 16 Python
Python采集爬取京东商品信息和评论并存入MySQL
Apr 12 Python
基于Keras 循环训练模型跑数据时内存泄漏的解决方式
Jun 11 #Python
什么是python的id函数
Jun 11 #Python
Keras:Unet网络实现多类语义分割方式
Jun 11 #Python
Pycharm中配置远程Docker运行环境的教程图解
Jun 11 #Python
Keras 快速解决OOM超内存的问题
Jun 11 #Python
python3.6.8 + pycharm + PyQt5 环境搭建的图文教程
Jun 11 #Python
使用keras实现孪生网络中的权值共享教程
Jun 11 #Python
You might like
PHP pathinfo()获得文件的路径、名称等信息说明
2011/09/13 PHP
PHP 冒泡排序 二分查找 顺序查找 二维数组排序算法函数的详解
2013/06/25 PHP
php微信支付之APP支付方法
2015/03/04 PHP
今天你说520了吗?不仅有php表白书还有java表白神器
2016/05/20 PHP
使用laravel指定日志文件记录任意日志
2019/10/17 PHP
代码生成器 document.write()
2007/04/15 Javascript
20个非常有用的PHP类库 加速php开发
2010/01/15 Javascript
ASP.NET jQuery 实例4(复制TextBox的文本到本地剪贴板上)
2012/01/13 Javascript
checkbox选中与未选中判断示例
2014/08/04 Javascript
javascript中使用new与不使用实例化对象的区别
2015/06/22 Javascript
jQuery基于cookie实现的购物车实例分析
2015/12/24 Javascript
浅析JS操作DOM的一些常用方法
2016/05/13 Javascript
js中document.referrer实现移动端返回上一页
2017/02/22 Javascript
vue bus全局事件中心简单Demo详解
2018/02/26 Javascript
解决vue-router中的query动态传参问题
2018/03/20 Javascript
vue自定v-model实现表单数据双向绑定问题
2018/09/03 Javascript
vuejs实现折叠面板展开收缩动画效果
2018/09/06 Javascript
vue src动态加载请求获取图片的方法
2018/10/17 Javascript
vue.js指令v-for使用以及下标索引的获取
2019/01/31 Javascript
ElementUI Tag组件实现多标签生成的方法示例
2019/07/08 Javascript
Vue 中 template 有且只能一个 root的原因解析(源码分析)
2020/04/11 Javascript
js实现简易拖拽的示例
2020/10/26 Javascript
[00:16]热血竞技场
2019/03/06 DOTA
python PIL模块与随机生成中文验证码
2016/02/27 Python
[原创]windows下Anaconda的安装与配置正解(Anaconda入门教程)
2018/04/05 Python
使用TensorFlow实现二分类的方法示例
2019/02/05 Python
H5混合开发app如何升级的方法
2018/01/10 HTML / CSS
某公司Java工程师面试题笔试题
2016/03/27 面试题
Servlet的实例是在生命周期什么时候创建的?配置servlet最重要的是什么?
2012/05/30 面试题
中等生评语大全
2014/05/04 职场文书
民政局副局长民主生活会个人整改措施
2014/10/04 职场文书
优秀的商业计划书,让融资一步到位
2019/05/07 职场文书
闭幕词的写作格式与范文!
2019/06/24 职场文书
golang中的空接口使用详解
2021/03/30 Python
golang中实现给gif、png、jpeg图片添加文字水印
2021/04/26 Golang
浅谈Redis在直播场景的实践方案
2021/04/27 Redis