0

我看到一篇关于不可变对象的文章。

它说何时:
variable = immutable
将不可变分配给变量。

例如
a = b # b is a immutable
它说在这种情况下a是指a copy of b,不是reference to b。如果 b 是mutable, 将aa reference to b

所以:

a = 10
b = a
a =20
print (b) #b still is 10

但在这种情况下:

a = 10
b = 10
a is b # return True
print id(10)
print id(a)
print id(b) # id(a) == id(b) == id(10)

ifa是 的 副本10,b也是 的 副本10, 为什么id(a) == id(b) == id(10)?

4

3 回答 3

6

“简单”的不可变文字(特别是 -1 和 255 之间的整数)是interned,这意味着即使绑定到不同的名称,它们仍然是同一个对象。

>>> a = 'foo'
>>> b = 'foo'
>>> a is b
True
于 2012-04-14T04:48:26.100 回答
5

虽然那篇文章对于某些语言可能是正确的,但对于 Python 来说是错误的。

当您在 Python 中进行任何正常分配时

some_name = some_name_or_object

你没有复制任何东西。您只需将名称指向作业右侧的对象。

可变性无关紧要。

更具体地说,原因:

a = 10
b = 10
a is b

is True, is that 10is interned - 这意味着 Python10在内存中保留一个,并且任何设置为10指向相同10的 .

如果你这样做

a = object()
b = object()
a is b

你会得到False,但是

a = object()
b = a
a is b

仍然会True

于 2012-04-14T04:46:52.590 回答
2

因为已经解释了实习,所以我将只讨论可变/不可变的东西:

将不可变对象分配给变量。

在谈论实际发生的事情时,我不会选择这种措辞。

我们有对象(存在于内存中的东西)和访问这些对象的方法:名称(或变量),它们被“绑定”到引用的对象。(你可以对物体说重点)

名称/变量是相互独立的,它们可以碰巧绑定到同一个对象,也可以绑定到不同的对象。重新定位一个这样的变量不会影响任何其他变量。

不存在按值传递或按引用传递这样的事情。在 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]- 它包含两次相同的对象。

于 2012-04-14T09:07:20.450 回答