我看到一篇关于不可变对象的文章。
它表示何时:
variable = immutable
将不可变赋值给变量。
a = b # b is a immutable
它表示在这种情况下a
引用a copy of b
,而不是reference to b
。
如果b为mutable
,a
将为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)
如果a
是10
的副本,b
也是10
的副本,为什么id(a) == id(b) == id(10)?
答案 0 :(得分:5)
虽然那篇文章可能对某些语言来说是正确的,但对于Python来说却是错误的。
在Python中执行任何正常分配时:
some_name = some_name_or_object
你没有复制任何东西。您只是将名称指向作业右侧的对象。
可变性无关紧要。
更具体地说,原因是:
a = 10
b = 10
a is b
是True
,10
是否被实习 - 意味着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)