📜  Python中的共享引用

📅  最后修改于: 2022-05-13 01:55:34.112000             🧑  作者: Mango

Python中的共享引用

让我们将变量 x 分配给值 5,并将另一个变量 y 分配给变量 x。

x = 5
y = x

当Python查看第一条语句时,它所做的是,首先,它创建一个对象来表示值 5。然后,如果变量 x 不存在,它会创建它,并使其成为对这个新对象 5 的引用。第二行导致Python创建变量 y,并且它没有分配 x,而是引用 x 所做的那个对象。最终结果是变量 x 和 y 最终引用了同一个对象。这种情况,多个名称引用同一个对象,在Python中称为共享引用
现在,如果我们写:

x = 'Geeks'

该语句创建一个新对象来表示“Geeks”,并使 x 引用这个新对象。但是,y 仍然引用原始对象,即 5。同样,如果我们再写一个语句:

b = 10

该语句导致创建一个新对象并使 y 引用这个新对象。先前对象所持有的空间如果不再被引用则被回收,即对象的空间被自动扔回空闲空间池中,以供将来的对象重用。
这种对象空间的自动回收称为垃圾收集

共享参考和就地更改

存在执行就地对象更改的对象和操作。例如,对列表中元素的赋值实际上会就地更改列表对象本身,而不是创建新的列表对象。对于支持就地更改的对象,您需要非常小心共享引用,因为其中的更改可能会影响其他对象。

L1 = [1, 2, 3, 4, 5]
  
L2 = L1

就像 x 和 y 一样,L1 和 L2 在语句 2 之后将引用同一个对象。如果我们改变 L1 中第 0 位的值,现在想想会发生什么,是只改变 L1 还是同时改变 L1 和 L2 ?

L1 = [1, 2, 3, 4, 5]
L2 = L1
  
L1[0] = 0
  
print(L1)
print(L2)

输出:

[0, 2, 3, 4, 5]
[0, 2, 3, 4, 5]

因此,L1 的变化反映回 L2。它不是为 L1 创建新对象,而是覆盖该位置的列表对象部分。这是一个就地改变。如果我们仍然想为 L2 维护一个单独的副本,这样 L1 中的任何更改都不会反映在 L2 中,那么我们可以请求Python为 L2 创建列表 L1 的副本。

L1 = [1, 2, 3, 4, 5]
L2 = L1[:]
  
L1[0] = 0
  
print(L1)
print(L2)

输出:

[0, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

注意:这种切片技术不适用于字典和集合。

由于 Python 的参考模型,在Python程序中有两种不同的方法来检查相等性。

L1 = [1, 2, 3, 4, 5]
L2 = L1
  
print(L1 == L2)
print(L1 is L2)

输出:

True
True

第一种方法, ==运算符测试引用的对象是否具有相同的值,如果它们具有相同的值,则返回 True,否则返回 False。第二种方法, is operator ,而是测试对象身份——仅当两个名称都指向完全相同的对象时,它才返回 True,因此基本上它是一种更强大的相等性测试形式。如果需要,它可以作为一种检测代码中共享引用的方法。如果名称指向等效但不同的对象,则返回 False。

现在,这里有一个棘手的部分:
看下面的代码,

L1 = [1, 2, 3, 4, 5]
L2 = [1, 2, 3, 4, 5]
  
print(L1 == L2)
print(L1 is L2)

输出:

True
False

如果我们对小数执行相同的操作会发生什么:

a = 50
b = 50
  
print(a == b)
print(a is b)

输出:

True
True

因为小整数和字符串被缓存和重用,所以它们引用同一个对象。而且由于您不能就地更改整数或字符串,因此对同一对象有多少引用并不重要。