python中可变和不可变对象(复值,拷贝,函数值传递)

 
 

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可变对象

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

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值