python中的不可变对象

时间:2012-04-14 04:44:18

标签: python variable-assignment immutability mutable

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

它表示何时:
variable = immutable
将不可变赋值给变量。

例如,

a = b # b is a immutable
它表示在这种情况下a引用a copy of b,而不是reference to b。 如果b为mutablea将为a 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)

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

4 个答案:

答案 0 :(得分:5)

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

在Python中执行任何正常分配时

some_name = some_name_or_object

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

可变性无关紧要。

更具体地说,原因是:

a = 10
b = 10
a is b

True10是否被实习 - 意味着Python在内存中保留一个10,并且设置为10的任何内容都指向同一个10 1}}。

如果你这样做

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

您将获得False,但

a = object()
b = a
a is b

仍为True

答案 1 :(得分:4)

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

>>> a = 'foo'
>>> b = 'foo'
>>> a is b
True

答案 2 :(得分:2)

因为已经解释了实习,我只会解决可变/不可变的事情:

  

将不可变赋值给变量。

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

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

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

没有通过值传递或通过引用传递的东西。在Python中,您始终传递/分配“按对象”。在为函数分配或传递变量时,Python永远不会创建副本,它总是传递/分配您已有的同一个对象。

现在,当您尝试修改不可变对象时,会发生什么?如前所述,该对象是不可变的,所以会发生以下情况:Python创建一个修改过的副本

至于你的例子:

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

这与可变性无关。在第一行,将int对象与值10绑定到名称a。在第二行,您将a引用的对象绑定到名称b

在第三行,将int对象与值20绑定到名称a,这不会更改名称b绑定的内容!

  

它说在这种情况下a指的是b的副本,而不是b的副本。如果b   是可变的,a是w的参考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

由于列表是可变的,我们可以更改小型,并查看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] - 它包含两次相同的对象。

答案 3 :(得分:1)