因为已经解释了实习,所以我将只讨论可变/不可变的东西:
将不可变对象分配给变量。
在谈论实际发生的事情时,我不会选择这种措辞。
我们有对象(存在于内存中的东西)和访问这些对象的方法:名称(或变量),它们被“绑定”到引用的对象。(你可以对物体说重点)
名称/变量是相互独立的,它们可以碰巧绑定到同一个对象,也可以绑定到不同的对象。重新定位一个这样的变量不会影响任何其他变量。
不存在按值传递或按引用传递这样的事情。在 Python 中,您总是“按对象”传递/分配。在将变量分配或传递给函数时,Python 从不创建副本,它总是传递/分配您已经拥有的相同对象。
现在,当您尝试修改不可变对象时,会发生什么?如前所述,该对象是不可变的,因此会发生以下情况:Python 创建一个修改后的副本。
至于你的例子:
a = 10
b = a
a =20
print (b) #b still is 10
这与可变性无关。在第一行,将带有值的 int 对象绑定10
到 name a
。在第二行,您将引用的对象绑定a
到 name b
。
在第三行,您将带有值的 int 对象绑定20
到 name a
,这不会更改 nameb
绑定的内容!
它说在这种情况下 a 是指 b 的副本,而不是对 b 的引用。如果 b 是可变的,则 a 将是对 b 的引用
如前所述,Python中没有引用之类的东西。Python 中的名称绑定到对象。不同的名称(或变量)可以绑定到同一个对象,但不同的名称本身之间没有联系。当你修改东西时,你修改了objects,这就是为什么绑定到该对象的所有其他名称“看到更改”,它们绑定到你修改过的同一个对象,对吧?
如果您将名称绑定到不同的对象,这就是发生的情况。其他名字没有魔法,它们保持原样。
至于带有列表的示例:
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 是一个包含一个项目的列表。这一项当然是一个对象。它是一个列表这一事实并没有让人联想到什么魔法,它只是一个简单的对象,恰好是一个列表,我们将它附加到 name 上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]
- 它包含两次相同的对象。