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发生了变化。
内存分配情况如下:
有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中有可变类型和不可变类型,这样的话,当传过来的是可变类型(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对象。
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]