因为实习已经解释过了,我只讲可变/不可变的东西:
将不可变对象分配给变量。
在谈论实际发生的事情时,我不会选择这种措辞。
我们有对象(存在于内存中的东西)和访问这些对象的方法:名称(或变量),它们被“绑定”到引用的对象。 (你可以说点对象)
名称/变量是相互独立的,它们可以碰巧绑定到同一个对象,也可以绑定到不同的对象。重新定位一个这样的变量不会影响任何其他变量。
没有值传递或引用传递这回事。在 Python 中,您总是“按对象”传递/分配。在将变量分配或传递给函数时,Python 从不创建副本,它总是传递/分配您已经拥有的相同对象。
现在,当您尝试修改不可变对象时,会发生什么?如前所述,对象是不可变的,因此发生的情况如下:Python 创建一个修改后的 copy。
至于你的例子:
a = 10
b = a
a =20
print (b) #b still is 10
这与可变性无关。在第一行,将值 10 的 int 对象绑定到名称 a。在第二行,将a 引用的对象绑定到名称b。
在第三行,您将值 20 的 int 对象绑定到名称 a,这不会更改名称 b 所绑定的内容!
在这种情况下,a 指的是 b 的副本,而不是 b 的引用。如果 b
是可变的,a 将是对 b 的引用
如前所述,Python 中没有 references 这样的东西。 Python 中的名称绑定到对象。不同的名称(或变量)可以绑定到同一个对象,但不同的名称本身之间没有联系。当您修改事物时,您修改了对象,这就是为什么绑定到该对象的所有其他名称“看到更改”,它们绑定到您修改的同一个对象,对吧?
如果您将名称绑定到不同的对象,那么就会发生这种情况。其他名称没有任何魔法,它们保持原样。
关于列表的例子:
In [1]: smalllist = [0, 1, 2]
In [2]: biglist = [smalllist]
In [3]: biglist
Out[3]: [[0, 1, 2]]
我可能写的是 In[1] 和 In[2],而不是:
In [1]: biglist = [[0, 1, 2]]
In [2]: smalllist = biglist[0]
这是等价的。
在这里看到的重要一点是 biglist 是一个包含一个项目的列表。这一项当然是一个对象。它是一个列表这一事实并没有让人联想到什么魔法,它只是一个恰好是一个列表的简单对象,我们将其附加到名称 smalllist。
因此,访问 biglist[i] 与访问 smalllist 完全相同,因为它们是同一个对象。我们从不复制,我们传递了对象。
In [14]: smalllist is biglist[0]
Out[14]: True
因为列表是可变的,所以我们可以改变 smallist,并在 biglist 中看到变化。为什么?因为我们实际上修改了smallist所指的对象。我们仍然拥有相同的对象(除了它已更改的事实)。但是 biglist 会“看到”这种变化,因为作为它的第一项,它引用了同一个对象。
In [4]: smalllist[0] = 3
In [5]: biglist
Out[5]: [[3, 1, 2]]
当我们将列表“加倍”时也是如此:
In [11]: biglist *= 2
In [12]: biglist
Out[12]: [[0, 1, 2], [0, 1, 2]]
发生的事情是这样的:我们有一个列表:[object1, object2, object3](这是一个通用示例)
我们得到的是: [object1, object2, object3, object1, object2, object3]:它只会插入(即修改“biglist”)列表末尾的所有项目。同样,我们插入对象,我们不会神奇地创建副本。
所以当我们现在更改 biglist 的第一项内的一项时:
In [20]: biglist[0][0]=3
In [21]: biglist
Out[21]: [[3, 1, 2], [3, 1, 2]]
我们也可以只更改smalllist,因为出于所有意图和目的,biglist 可以表示为:[smalllist, smalllist]——它包含两次相同的对象。