python中的不可变对象
我看到一篇关于不可变对象的文章 它说什么时候:python中的不可变对象,python,variable-assignment,immutability,mutable,Python,Variable Assignment,Immutability,Mutable,我看到一篇关于不可变对象的文章 它说什么时候: 变量=不可变的 将不可变的赋值给变量 例如 a=b#b是不可变的 在这种情况下,a指的是b的副本,而不是指的是b。 如果b是可变的,a将是对b的引用 因此: 但在这种情况下: 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)=
变量=不可变的
将不可变的赋值给变量 例如
a=b#b是不可变的
在这种情况下,
a
指的是b的副本
,而不是指的是b
。
如果b是可变的,a将是对b的引用
因此:
但在这种情况下:
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)?
虽然这篇文章对某些语言可能是正确的,但对Python来说是错误的
在Python中执行任何常规赋值时:
你没有复制任何东西。您只是将名称指向赋值右侧的对象
可变性是不相关的
更具体地说,原因是:
a = 10
b = 10
a is b
是True
,是10
是interned吗?这意味着Python在内存中保留一个10
,任何设置为10
的对象都指向相同的10
如果你这样做
a = object()
b = object()
a is b
您将得到False
,但是
a = object()
b = a
a is b
仍然是True
“简单”不可变文本(尤其是-1和255之间的整数)是内部的,这意味着即使绑定到不同的名称,它们仍然是相同的对象
>>> a = 'foo'
>>> b = 'foo'
>>> a is b
True
In [4]: smalllist[0] = 3
In [5]: biglist
Out[5]: [[3, 1, 2]]
因为实习已经解释过了,所以我只讨论可变/不变的东西: 将不可变的赋值给变量 当谈到实际发生的事情时,我不会选择这样的措辞 我们有对象(存在于内存中的东西)和访问这些对象的方法:名称(或变量),它们“绑定”到引用中的对象。(可以说是指向对象的点) 这些名称/变量彼此独立,它们可以绑定到同一个对象,也可以绑定到不同的对象。重新定位一个这样的变量不会影响任何其他变量 没有按值传递或按引用传递这样的事情。在Python中,您总是通过“对象”传递/分配。在向函数赋值或传递变量时,Python从不创建副本,它总是传递/分配与您已经拥有的对象完全相同的对象 现在,当您尝试修改一个不可变对象时,会发生什么?如前所述,对象是不可变的,因此所发生的情况是:Python创建一个修改后的副本 至于你的例子:
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中没有引用这样的东西。Python中的名称绑定到对象。不同的名称(或变量)可以绑定到同一个对象,但不同名称之间没有联系。当你修改东西的时候,你就修改了对象,这就是为什么绑定到该对象的所有其他名称都“看到了变化”,它们绑定到了你修改过的同一个对象,对吗
如果将一个名称绑定到另一个对象,就会发生这种情况。其他名字没有变魔术,他们保持原样
关于列表示例:
In [1]: smalllist = [0, 1, 2]
In [2]: biglist = [smalllist]
In [3]: biglist
Out[3]: [[0, 1, 2]]
而不是在[1]和[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将“看到”这一变化,因为它的第一项引用的是同一个对象
>>> a = 'foo'
>>> b = 'foo'
>>> a is b
True
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]
——它包含了两次相同的对象。这并不能解释他文章的前三分之二,只有最后三分之一。@agf:最后三分之一是有问题的部分。仍然需要指出的是,他从他读到的文章中得出的结论是错误的。我并不反对措辞含糊不清。Python试图对int
值进行内部处理,但并不总是成功。尝试a=1000
和b=999
,然后再尝试b+=1
。当我刚才尝试时,a
和b
分别绑定到一个值为1000的不同int
对象。不过,我认为对于值0和1,实习将始终成功。这种情况下:lst=[0,1,2]*2
,列表