python中可变和不可变对象(复值,拷贝,函数值传递)
2017-11-22 10:38
204 查看
python中有可变对象和不可变对象,
可变对象: list, dict.
不可变对象有: 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 #Falseif __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 发生了变化。
内存分配情况如下:
内存变化如下:
好啦!答案显而易见啦,经过 test() 方法修改后,传递过来的 int 类型外部变量没有发生改变,而 list 这种可变类型则因为 test() 方法的影响导致内容发生了改变。
ps:
值传递:表示传递直接传递变量的值,把传递过来的变量的值复制到形参中,这样在函数内部的操作不会影响到外部的变量
引用传递:我个人觉得可以把引用理解为一个箭头,这个箭头指向某块内存地址,而引用传递,传递过来的就是这个箭头,当你修改内容的时候,就是修改这个箭头所指向的内存地址中的内容,因为外部也是指向这个内存中的内容的,所以,在函数内部修改就会影响函数外部的内容。
原文链接 http://www.jianshu.com/p/c5582e23b26c
可变对象: 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 #Falseif __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可变对象
dict,list
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时,就可以收回这块内存了。当然我觉得它肯定不止用了计数器吧,应该还有其他的技术,比如分代回收什么的。不再讨论之列,就这样了...ps:
值传递:表示传递直接传递变量的值,把传递过来的变量的值复制到形参中,这样在函数内部的操作不会影响到外部的变量
引用传递:我个人觉得可以把引用理解为一个箭头,这个箭头指向某块内存地址,而引用传递,传递过来的就是这个箭头,当你修改内容的时候,就是修改这个箭头所指向的内存地址中的内容,因为外部也是指向这个内存中的内容的,所以,在函数内部修改就会影响函数外部的内容。
原文链接 http://www.jianshu.com/p/c5582e23b26c
相关文章推荐
- python的raw_input()函数。 函数的可变对象和不可变对象作为参数传递。
- Python基础:Python可变对象和不可变对象
- Python函数可变参数定义及其参数传递方式实例详解
- 【Python】构造函数、析构函数与可变参数传递
- Python中函数个数可变参数的传递
- Python中的可变与不可变对象
- Python函数可变参数定义及其参数传递方式实例详解
- python(三)字典,集合,可变与不可变对象(上)
- Leetcode 39: python可变类型复制(浅拷贝和深拷贝)
- 流畅的python学习笔记第八章:深拷贝,浅拷贝,可变参数
- python函数是引用传递(对可变对象而言)
- python可变对象与不可变对象
- Python-可变对象和不可变对象
- python中函数参数,函数调用参数传递,可变参数,*expression,**expression
- Python中函数的参数传递与可变长参数
- Python学习笔记7-把函数当参数传递、指定可变参数
- python 函数传递可变参数的用法
- python(三)字典,集合,可变与不可变对象(上)
- python 可变参数传递
- python(四)字典,集合,可变与不可变对象(下)