Python变量赋值时的引用传递问题

变量赋值时的引用传递

  在Python语言中,对象是通过引用传递的。在赋值时,不管这个对象是新创建的,还是一个已经存在的,都是将该对象的引用(并不是值)赋值给变量。

  要保持追踪内存中的对象,Python使用了引用计数这一简单技术。也就是说Python内部记录着所有使用中的对象各有多少引用。一个内部跟踪变量,称为一个引用计数器。每个对象各有多少个引用,简称引用计数。当对象被创建时,就创建了一个引用计数,当这个对象不再需要时,也就是说,这个对象的引用计数变为0时,它被垃圾回收。

  当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为1。当同一个对象(的引用)又被赋值给其他变量时,或作为参数传递给函数、方法或类实例时,或者被赋值为一个窗口对象的成员时,该对象的一个新的引用,或者称作别名,就被创建(则该对象的引用计数自动加1)。

  当对象的引用被销毁时,引用计数会减少。最明显的例子就是当引用离开其作用范围时,这种情况最经常出现在函数运行结束时,所有局部变量都被自动销毁,对象的引用计数也就随之减少。当变量被赋值给另外一个对象时,原对象的引用计数也会自动减1。其他造成对象的引用计数减少的方式包括使用del语句删除一个变量,或者当一个对象被移出一个窗口对象时(或该容器对象本身的引用计数变成了0时)。

>>> a = 1
>>> b = a
>>> id(a), id(b)
(1386769424, 1386769424)
>>> a = 2
>>> a, b
(2, 1)
>>> id(a), id(b)
(1386769456, 1386769424)

  在上面的例子中,a = 1使变量a指向了整型对象1b = a使变量b也指向了整型对象1,通过语句id(a), id(b)可以看到变量a和变量b所指向的内存地址相同。

  语句a = 2将新的对象2赋值给了变量a,此时变量a指向了对象2,变量b仍然指向原来的对象1。通过语句id(a), id(b)可以看到变量a与变量b所指向的内存地址不同。

可变对象和不可变对象

  Python语言中的对象分为可变对象和不可变对象。

  • 不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。

  • 可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟出新的地址,通俗点说就是原地改变。

  数字、字符串和元组是不可变类型,列表和字典是可变类型。

>>> a = 1
>>> id(a)
1386769424
>>> a = a + 1
>>> a
2
>>> id(a)
1386769456

  在上面的例子中,语句a = a + 1改变变量a的值时,由于变量a所指向的对象1是不可变对象,因而创建了新的对象2并使变量a指向了对象2

>>> a = (1, 2, 3)
>>> id(a)
305934860864
>>> a = a + (4, 5, 6)
>>> a
(1, 2, 3, 4, 5, 6)
>>> id(a)
305934762152

  可以看到使用元组的结果与整型对象相同。

>>> a = [1, 2, 3]
>>> b = a
>>> id(a), id(b)
(305934751624, 305934751624)
>>> b += [4, 5, 6]
>>> a, b
([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6])
>>> id(a), id(b)
(305934751624, 305934751624)

  上面是可变对象的一个例子,变量a和变量b同时指向了列表对象[1, 2, 3],当语句b += [4, 5, 6]改变变量b的值时,并没有创建新的列表对象,而是直接改变了原先变量a和变量b所指向的列表对象。

  在Python中a += ba = a + b是有区别的。a += b直接对变量a进行操作,会改变变量a的值。a = a + b中,a + b会生成一个新的对象,变量a将指向这个新对象。

>>> a = [1, 2, 3]
>>> b = a
>>> id(a), id(b)
(305934751176, 305934751176)
>>> b = b + [4, 5, 6]
>>> a, b
([1, 2, 3], [1, 2, 3, 4, 5, 6])
>>> id(a), id(b)
(305934751176, 305939124168)

Python内存分配

  Python中会为每个出现的对象分配内存,哪怕他们的值完全相等(注意是相等不是相同)。如执行a = 2.0b = 2.0这两个语句时会先后为2.0这个Float类型对象分配内存,然后将ab分别指向这两个对象。所以ab指向的不是同一对象:

>>> a = 2.0
>>> b = 2.0
>>> a is b
False
>>> a == b
True

  但是为了提高内存利用效率对于一些简单的对象,如一些数值较小的int对象,Python采取重用对象内存的办法,如执行a = 2b = 2时,由于2作为简单的int类型且数值小,Python不会两次为其分配内存,而是只分配一次,然后将ab同时指向已分配的对象:

>>> a = 2
>>> b = 2
>>> a is b
True

  如果赋值的不是2而是大的数值,情况就跟前面的一样了:

>>> a = 5555
>>> b = 5555
>>> a is b
False
>>> id(a)
12464372
>>> id(b)
12464396

  两个对象即便值相等,但仍然是不同的对象。

>>> id(12345)
305935347440
>>> id(12345)
305935347152

参考文献
[1] Python核心编程(第二版)
[2] https://www.cnblogs.com/sun-haiyu/p/7096918.html
[3] https://blog.csdn.net/weixin_36250487/article/details/79620874

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值