Python 学习之共享引用

文章目录

什么是共享引用

假设我们在Python交互模式下输入以下语句:

>>> a = 3
>>> b = a
  • 1
  • 2

在这里插入图片描述

实际的效果就是变量a和b都引用了相同的对象(指向了相同的内存空间)。这在Python中叫做共享引用——多个变量名引用了同一个对象。

如果再来一条语句

>>> a = 'spam'
  • 1

会怎么样?

变量a引用了由常量表达式‘spam’所创建的新对象,但是变量b仍然引用原始的对象3,因为这个赋值运算改变的不是对象3,它仅仅改变了变量a的指向,变量b并没有发生改变。

在这里插入图片描述

在Python中,变量总是一个指向对象的指针,而不是可改变的内存区域的标签(比如C语言中的变量)。给一个变量赋一个新值,并不是修改了原始的对象,而是让这个变量去引用完全不同的一个对象。

注意:当可变的对象以及原处的改变进入这个场景,上述情形会有某种改变。

共享引用和在原处修改(Shared References and In-Place Changes)

有一些对象和操作确实会在原处改变对象。例如,在一个列表中通过偏移进行赋值,这确实会改变这个列表对象,而不是生成一个新的列表对象。对于支持这种在原处修改的对象,共享引用的时候一定要小心,因为对一个变量的修改会影响其他变量。

扫描二维码关注公众号,回复: 9960144 查看本文章

请看下面的语句:

>>> L1 = [2, 3, 4]
>>> L2 = L1
  • 1
  • 2

L1 是一个包含了对象2、3、4的列表。列表中的元素是通过他们的位置进行读取的,所以L1[0]引用对象2. 当然,列表自身也是对象,就像整数和字符串一样。在运行了上面两行语句后,L1和L2引用了相同的对象。

现在加上第3行:

>>> L1 = [2, 3, 4]
>>> L2 = L1
>>> L1[0] = 24
  • 1
  • 2
  • 3

我们看一下L1和L2的值:

>>> L1
[24, 3 ,4]
>>> L2
[24, 3 ,4]
  • 1
  • 2
  • 3
  • 4

在这里,没有改变L1,改变了L1所引用的对象的一个元素,这类修改会覆盖列表对象中的某部分。因为这个列表对象同时被L1和L2引用,所以在原处修改不仅仅会影响L1,也会影响L2。虽然我们没有改变L2,但是它的值将发生变化。

如果你不想要这样的结果,那么需要拷贝对象,而不是创建引用。有很多拷贝列表的办法,最简单的办法是从头到尾的切片。

>>> L1 = [2, 3, 4]
>>> L2 = L1[:]
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[2, 3, 4]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里,对L1的修改不会影响L2,因为L2引用的是L1所引用对象的一个拷贝。也就是说,L1和L2指向了不同的内存区域。

共享引用和相等

>>> x = 42
>>> x = 'shrubbery'    
  • 1
  • 2

因为Python缓存并复用了小的整数和小的字符串,执行完这两行代码后,对象42也许不会被回收;相反地,它可能仍被保存在一个系统表中,等待下一次你的代码生成另一个42来重复利用。尽管这样,大多数种类的对象都会在不被引用的时候马上回收。

在Python中有2种不同的方法去检查两个变量是否相等。

>>> L = [1, 2, 3]
>>> M = L                 # M and L reference the same object
>>> L == M                # Same values
True
>>> L is M                # Same objects
True
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

==操作符测试两个被引用的对象是否有相同的值,这种方法往往在Python中用作相等的检查。

is操作符用来检查对象的同一性。如果两个变量名都指向同一个对象,则会返回 True,所以这是一种更严格的相等测试。

实际上,is只是比较实现引用的指针,所以这是一种检测共享引用的方法。如果变量名指向不同的对象,就算这两个对象的值相等,也会返回 False.

例如:

>>> L = [1, 2, 3]
>>> M = [1, 2, 3]         # M and L reference different objects
>>> L == M                # Same values
True
>>> L is M                # Different objects
False
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果对小的数字进行类似测试:

>>> X = 42
>>> Y = 42                # Should be two different objects
>>> X == Y
True
>>> X is Y                # Same object anyhow: caching at work!
True
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

按理来说,第3~4行是可以理解的,因为两个对象的值一样;但是第5~6行就让人匪夷所思了,X引用的42和Y引用的42本来是2个对象,应该输出False才对,不过,因为小的整数和字符串被缓存并复用了,所以is告诉我们X和Y引用了同一个对象。

实际上,你可以用sys模块中的getrefcount函数查询对象的被引用次数。例如,我们查一下整数1被引用的次数:

>>> import sys
>>> sys.getrefcount(1)   
812
  • 1
  • 2
  • 3

这种对象缓存和复用的机制与代码是没有关系的。Python这样做是为了提高执行速度。

发布了24 篇原创文章 · 获赞 30 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/yufen9987/article/details/103036520
今日推荐