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实现给字典添加条目的方法
Sep 25 Python
python中实现定制类的特殊方法总结
Sep 28 Python
python实现定时播放mp3
Mar 29 Python
python实现的用于搜索文件并进行内容替换的类实例
Jun 28 Python
python装饰器简介---这一篇也许就够了(推荐)
Apr 01 Python
Python中顺序表原理与实现方法详解
Dec 03 Python
python入门之基础语法学习笔记
Feb 08 Python
python爬虫实现获取下一页代码
Mar 13 Python
Django之choices选项和富文本编辑器的使用详解
Apr 01 Python
python图片验证码识别最新模块muggle_ocr的示例代码
Jul 03 Python
python 爬取吉首大学网站成绩单
Jun 02 Python
Python爬取某拍短视频
Jun 11 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使用socket post数据到其它web服务器的方法
2015/06/02 PHP
jQuery formValidator表单验证插件开源了 含API帮助、源码、示例
2008/08/14 Javascript
ASP.NET jQuery 实例1(在TextBox里面创建一个默认提示)
2012/01/13 Javascript
jQuery动态添加 input type=file的实现代码
2012/06/14 Javascript
JS短路原理的应用示例 精简代码的途径
2013/12/13 Javascript
一个字符串反转函数可实现字符串倒序
2014/09/15 Javascript
浅谈JavaScript中的Math.atan()方法的使用
2015/06/14 Javascript
详解JavaScript跨域总结与解决办法
2016/10/31 Javascript
vue实现简单表格组件实例详解
2017/04/16 Javascript
Javascript 实现匿名递归的实例代码
2017/05/25 Javascript
vue事件修饰符和按键修饰符用法总结
2017/07/25 Javascript
详解vue 模拟后台数据(加载本地json文件)调试
2017/08/25 Javascript
加载 vue 远程代码的组件实例详解
2017/11/20 Javascript
JS文件中加载jquery.js的实例代码
2018/05/05 jQuery
基于ionic实现下拉刷新功能
2018/05/10 Javascript
[05:13]TI4 中国战队 机场出征!!
2014/07/07 DOTA
[01:02:20]Mineski vs TNC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
Python内置函数—vars的具体使用方法
2017/12/04 Python
Python Pandas找到缺失值的位置方法
2018/04/12 Python
解决Django的request.POST获取不到内容的问题
2018/05/28 Python
Python使用matplotlib实现基础绘图功能示例
2018/07/03 Python
python tkinter图形界面代码统计工具(更新)
2019/09/18 Python
Python 2种方法求某个范围内的所有素数(质数)
2020/01/31 Python
Python查找不限层级Json数据中某个key或者value的路径方式
2020/02/27 Python
150行Python代码实现带界面的数独游戏
2020/04/04 Python
JAVA及PYTHON质数计算代码对比解析
2020/06/10 Python
标签和贴纸印刷:Lightning Labels
2018/03/22 全球购物
运动鞋、足球鞋和慕尼黑球衣:Sport Münzinger
2019/08/26 全球购物
iostream与iostream.h的区别
2015/01/16 面试题
大三自我鉴定范文
2013/10/05 职场文书
喝酒检查书范文
2014/02/23 职场文书
党的群众路线教育实践活动心得体会
2014/03/03 职场文书
环保建议书300字
2014/05/14 职场文书
小学关爱留守儿童活动方案
2014/08/25 职场文书
数学教师求职信范文
2015/03/20 职场文书
2016年共产党员个人承诺书
2016/03/24 职场文书